classA: b =0def__init__(self): self.a =1@classmethoddeffoo(cls,a):print(a)@classmethoddefbar(cls,a): cls.b += aprint(cls.b)A.bar(3)A.bar(2)
自定义装饰器
例子1:
defmy_decorator(func):defwrapper_function(*args,**kwargs):print("*"*10) res =func(*args, **kwargs)print("*"*10)return resreturn wrapper_function@my_decoratordeffoo(a):return a# 相当于foo=my_decorator(foo)x =foo(1)
例子2:
from functools import wrapsdefnode_func(name):defdecorate(func):@wraps(func)defwrapper(*args,**kwargs):if name =="A":# in self.nodes_df.columns:return1# dict(self.nodes_df[name])else:returnfunc(*args, **kwargs)return wrapperreturn decorate# 等价于:foo1 = node_func("A")(foo1)@node_func("A")deffoo1(a):return"a"@node_func("B")defbar1(a):return"a"
Return the __dict__ attribute for a module, class, instance, or any other object with a __dict__ attribute.
Objects such as modules and instances have an updateable __dict__ attribute; however, other objects may have write restrictions on their __dict__ attributes (for example, classes use a types.MappingProxyType to prevent direct dictionary updates).
Without an argument, vars() acts like locals(). Note, the locals dictionary is only useful for reads since updates to the locals dictionary are ignored.
A TypeError exception is raised if an object is specified but it doesn’t have a __dict__ attribute (for example, if its class defines the __slots__ attribute).
注意:假设类 B 继承自定义了 __slots__ 的类 A,那么子类 B 的实例不会受到父类 __slots__ 的限制。
2.5 内置函数 dir 与 __dir__ 方法
从设计理念上说:不同于 vars 与 __dict__,dir 方法倾向于给出全部信息:包括特殊方法名
dir 函数返回的是一个标识符名列表,逻辑是:首先寻找 __dir__ 函数的定义(object 类中有着默认的实现),若存在 __dir__ 函数,则返回 list(x.__dir__())。备注:__dir__ 函数必须定义为一个可迭代对象。
若该类没有自定义 __dir__ 函数,则使用 object 类的实现逻辑,大略如下:
If the object does not provide __dir__(), the function tries its best to gather information from the object’s __dict__ attribute, if defined, and from its type object. The resulting list is not necessarily complete, and may be inaccurate when the object has a custom __getattr__().
The default dir() mechanism behaves differently with different types of objects, as it attempts to produce the most relevant, rather than complete, information:
If the object is a module object, the list contains the names of the module’s attributes.
If the object is a type or class object, the list contains the names of its attributes, and recursively of the attributes of its bases.
Otherwise, the list contains the object’s attributes’ names, the names of its class’s attributes, and recursively of the attributes of its class’s base classes.
classA:def__getattribute__(self,name):print(f"enter __getattribute__({name})")if name =="a.b":return nameprint(f"call object.__getattribute__({name})")returnobject.__getattribute__(self, name)def__getattr__(self,name):print(f"enter __getattr__({name})")if name =="a.c":return nameelse:raiseAttributeError("custom error info: '{}' object has no attribute '{}'".format(type(self).__name__, name))a =A()# 无输出a.__getattribute__("a.b")# 成功返回# enter __getattribute__(__getattribute__)# call object.__getattribute__(__getattribute__)# enter __getattribute__(a.b)getattr(a, "a.b")# 成功返回# enter __getattribute__(a.b)a.data # 成功返回# enter __getattribute__(data)# call object.__getattribute__(data)a.x # 成功返回# enter __getattribute__(x)# call object.__getattribute__(x)# enter __getattr__(x)a.y # 报错: custom error info: 'A' object has no attribute 'y'# enter __getattribute__(y)# call object.__getattribute__(y)# enter __getattr__(y)getattr(a, "a.c")# 成功返回# enter __getattribute__(a.c)# call object.__getattribute__(a.c)# enter __getattr__(a.c)a.__getattribute__("a.c")# 报错: 'A' object has no attribute 'a.c'# enter __getattribute__(__getattribute__)# call object.__getattribute__(__getattribute__)# enter __getattribute__(a.c)# call object.__getattribute__(a.c)a.__getattr__("y")# 报错: custom error info: 'A' object has no attribute 'y'# enter __getattribute__(__getattr__)# call object.__getattribute__(__getattr__)# enter __getattr__(y)
The starting point for descriptor invocation is a binding, a.x. How the arguments are assembled depends on a:
Direct Call
The simplest and least common call is when user code directly invokes a descriptor method: x.__get__(a).
Instance Binding
If binding to an object instance, a.x is transformed into the call: type(a).__dict__['x'].__get__(a, type(a)).
Class Binding
If binding to a class, A.x is transformed into the call: A.__dict__['x'].__get__(None, A).
Super Binding
If a is an instance of super, then the binding super(B, obj).m() searches obj.__class__.__mro__ for the base class A immediately preceding B and then invokes the descriptor with the call: A.__dict__['m'].__get__(obj, obj.__class__).
{'color':'red'}{'__module__':'__main__','number_of_weels':4,'__init__':<function Car.__init__ at 0x000001A3C7857040>,'__doc__':None}{'color':'red','bar':<function foo at 0x000001A3C76ED160>}{'__module__':'__main__','number_of_weels':4,'__init__':<function Car.__init__ at 0x000001A3C7857040>,'__doc__':None}foo
classOneDigitNumericValue():def__set_name__(self,owner,name):# owner is Foo, name is number self.name = namedef__get__(self,obj,type=None) ->object:return obj.__dict__.get(self.name)or0def__set__(self,obj,value) ->None: obj.__dict__[self.name] = valueclassFoo(): number =OneDigitNumericValue()my_foo_object =Foo()my_second_foo_object =Foo()my_foo_object.number =3print(my_foo_object.number)print(my_second_foo_object.number)my_third_foo_object =Foo()print(my_third_foo_object.number)
实用例子
避免重复使用 property
classValues:def__init__(self): self._value1 =0 self._value2 =0 self._value3 =0@propertydefvalue1(self):return self._value1@value1.setterdefvalue1(self,value): self._value1 = value if value %2==0else0@propertydefvalue2(self):return self._value2@value2.setterdefvalue2(self,value): self._value2 = value if value %2==0else0@propertydefvalue3(self):return self._value3@value3.setterdefvalue3(self,value): self._value3 = value if value %2==0else0my_values =Values()my_values.value1 =1my_values.value2 =4print(my_values.value1)print(my_values.value2)
unless you make strong use of multiple inheritance and you have non-trivial hierarchies, you don't need to understand the C3 algorithm, and you can easily skip this paper.
classVerifySnakeCase:# cls 是 Animal, name 是 "animal", kwargs 是 {}def__init_subclass__(cls,name,**kwargs):print(cls, name, kwargs)super().__init_subclass__(**kwargs)# 注意 object.__init_subclass__ 实际上只能接收 0 个参数 cls.name = name not_camel_case =set()for ele in cls.__dict__:if cls._not_snake_case(ele)and ele notin not_camel_case: not_camel_case.add(ele)if not_camel_case:raiseValueError(f'The following members are not in snake case: {", ".join(not_camel_case)}')@classmethoddef_not_snake_case(cls,txt):return txt.lower()!= txtclassAnimal(VerifySnakeCase,name="animal"):def__init__(self,a,b): self.a = a self.b = bdefeat_method(self):print('This animal can eat.')defsleep_method(self):print('This animal can sleep.')Dog =type("Dog", (VerifySnakeCase,), {}, name="dog")# 此时可以使用第 4 个参数
classMetaA(type):def__new__(cls,name,bases,dct):print('MetaA.__new__ begin') t =type(name, bases, dct)print('MetaA.__new__ end', t)return tdef__init__(cls,name,bases,dct):print('MetaA.__init__')classA(object,metaclass=MetaA): pass"""MetaA.__new__ beginMetaA.__new__ end <class '__main__.A'>"""classMetaA(type):def__new__(cls,name,bases,dct):print('MetaA.__new__ begin') t =type.__new__(cls, name, bases, dct)print('MetaA.__new__ end', t)return tdef__init__(cls,name,bases,dct):print('MetaA.__init__')classA(object,metaclass=MetaA): pass"""MetaA.__new__ beginMetaA.__new__ end <class '__main__.A'>MetaA.__init__"""
object.__new__ 函数与 object.__init__ 函数
以下是一个代码样例:
classA(object):def__init__(self,*args,**kwargs):print("run the init of A")def__new__(cls,*args,**kwargs):print(f"run the new of A, parameters: {cls}")returnobject.__new__(B)classB(object):def__init__(self,*args,**kwargs):print("run the init of B")print(f"extra parameters for __init__: {args}, {kwargs}")print("id in __init__", id(args[0]), args, id(kwargs)) self.args = args self.kwargs = kwargsdef__new__(cls,*args,**kwargs):print("run the new of B", cls)print(f"extra parameters for __new__: {args}, {kwargs}")print("id in __new__ start", id(args[0]), args, id(kwargs)) args[0]["b"] =3# 如果直接用 args = ({"a": 2, "b": 3},) 是没有效果的print("id in __new__ after", id(args[0]), args, id(kwargs))returnobject.__new__(cls)# object.__new__ 只能有一个参数a =A()# 只调用了 A.__new__ 就结束了print(type(a))# <class '__main__.B'>print("===============")b =B({"a": 2}, c =2)# 执行逻辑: __new__ 的 cls 参数自动用 B 填充. 伪代码猜测如下# def _construct_guess(*args, **kwargs):# ret = B.__new__(B, *args, **kwargs)# if isinstance(ret, B):# B.__init__(ret, *args, **kwargs) # return ret# 实参传递如下# b = B.__new__(B, args=({"a": 2},), kwargs={"c": 2})# B.__init__(b, args=({"a": 2, "b": 3},), kwargs={"c": 2})print(type(b), b.args, b.kwargs)
输出结果
run the new of A, parameters: <class '__main__.A'>
<class '__main__.B'>
===============
run the new of B <class '__main__.B'>
extra parameters for __new__: ({'a': 2},), {'c': 2}
id in __new__ start 139811860204800 ({'a': 2},) 139811860203200
id in __new__ after 139811860204800 ({'a': 2, 'b': 3},) 139811860203200
run the init of B
extra parameters for __init__: ({'a': 2, 'b': 3},), {'c': 2}
id in __init__ 139811860204800 ({'a': 2, 'b': 3},) 139811860203200
<class '__main__.B'> ({'a': 2, 'b': 3},) {'c': 2}
from abc import abstractmethod, ABCMeta, ABC# class Model(metaclass=ABCMeta):classModel(ABC):@abstractmethoddeffoo(self):"""This method foos the model."""
# 1) without using with statementfile =open('file_path', 'w')file.write('hello world !')file.close()# 2) without using with statementfile =open('file_path', 'w')try: file.write('hello world')finally: file.close()# 3) using with statementwithopen('file_path', 'w')as file: file.write('hello world !')
# 获取[1, n]中的所有素数for n inrange(2, 10):for x inrange(2, n):if n % x ==0:print( n, 'equals', x, '*', n/x)breakelse:# loop fell through without finding a factorprint(n, 'is a prime number')# 来源于Cython文档里的例子
deffoo(a,b,/, c,d=3,*args,e=5,f,**kwargs): passfor name, p in inspect.signature(foo).parameters.items():print(name, p.kind.__str__())# 打印结果# a POSITIONAL_ONLY# b POSITIONAL_ONLY# c POSITIONAL_OR_KEYWORD# d POSITIONAL_OR_KEYWORD# args VAR_POSITIONAL# e KEYWORD_ONLY# f KEYWORD_ONLY# kwargs VAR_KEYWORD
This function is invoked by the import statement. It can be replaced (by importing the builtins module and assigning to builtins.__import__) in order to change semantics of the import statement, but doing so is strongly discouraged as it is usually simpler to use import hooks (see PEP 302) to attain the same goals and does not cause issues with code which assumes the default import implementation is in use. Direct use of __import__() is also discouraged in favor of importlib.import_module().
任何对象都可以进行 Truth Value Testing(真值测试),即用于 bool(x) 或 if 或 while 语句,具体测试流程为,首先查找该对象是否有 __bool__ 方法,若存在,则返回 bool(x) 的结果。然后再查找是否有 __len__ 方法,若存在,则返回 len(x)!=0 的结果。若上述两个方法都不存在,则返回 True。
file_name ="techcrunch.csv"lines = (line for line inopen(file_name))list_line = (s.rstrip().split(",")for s in lines)cols =next(list_line)company_dicts = (dict(zip(cols, data))for data in list_line)funding = (int(company_dict["raisedAmt"])for company_dict in company_dictsif company_dict["round"]=="a")total_series_a =sum(funding)print(f"Total series A fundraising: ${total_series_a}")
deflist_gen(): data = [1,2,3]for x in data:print("x", x)yield xit =list_gen()next(it)it.close()# 之后再度调用 next(it) 时会触发 StopIteration, 因此后面的 for 不会打印内容for i in it:print("i", i)
throw
deflist_gen(): data = [1,2,3]for x in data:print("x", x)try:yield xexceptValueErroras err:print(err)it =list_gen()next(it)# 打印内容如下# x: 1it.throw(ValueError("stop"))# 打印内容如下, 注意不完全等同于 send(ValueError("stop"))# x: 2# stopnext(it)# x: 3next(it)# 触发 StopIteration
python 中还有一个关键字 yield from, 虽然在简单场景下, yield from it 似乎跟 for i in it: yield i 没太大区别, 但实际上, 在 send, close, throw 方法上, 还是有区别的, 参考这个问答, 这里仅举一例:
defwriter():"""A coroutine that writes data *sent* to it to fd, socket, etc."""whileTrue: w = (yield)print('>> ', w)defwriter_wrapper(coro):yield from coro# for i in coro:# yield iw =writer()wrap =writer_wrapper(w)wrap.send(None)# "prime" the coroutinefor i inrange(4): wrap.send(i)# 注意这里是对 wrap 调用 send, 如果改成对 w 调用 send, 那么在这个例子中, yield from 和 for 都能得到一样的结果, 然而通常情况下我们没有办法拿到 w 这个变量, 而只能对 wrap 进行操作, 所以 yield from 实际上相当于建立了这里的 send 到 w 的隧道
执行结果
>> 0
>> 1
>> 2
>> 3
如果不使用 yield from, 那么执行结果将是:
>> None
>> None
>> None
>> None
引用上面这个问答的理解:
What yield from does is it establishes a transparent bidirectional connection between the caller and the sub-generator
If a name binding operation occurs anywhere within a code block, all uses of the name within the block are treated as references to the current block. This can lead to errors when a name is used within a block before it is bound. This rule is subtle. Python lacks declarations and allows name binding operations to occur anywhere within a code block. The local variables of a code block can be determined by scanning the entire text of the block for name binding operations. See the FAQ entry on UnboundLocalError for examples.
这里是解释器先看了整个 code block, 即先看了 print(var) 之后的 var=200 这条语句, 认为 var 应该是一个 local variable, 所以在真正执行时按从上到下, 在执行 print(var) 时发现局部变量 var 没有被定义, 引发报错
var =100# A global variabledefincrement(): var =2# OK, local variable
If a name is bound in a block, it is a local variable of that block, unless declared as nonlocal or global. If a name is bound at the module level, it is a global variable. (The variables of the module code block are local and global.) If a variable is used in a code block but not defined there, it is a free variable.
Operationally, a closure is a record storing a function together with an environment. The environment is a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.
注意这里的 free variable, used locally, enclosing scope 都是站在 inner function 的视角来看待的, 简单来说:
closure 包含 inner function 和它的 free variable
closure 在被调用时的特点如下:
Unlike a plain function, a closure allows the function to access those captured variables through the closure's copies of their values or references, even when the function is invoked outside their scope.
defgenerate_power(exponent): # `generate_power` is enclosing function (higher-order function, closure factory function, outer function)defpower(base): # `power` is inner function (nested function)return base ** exponentreturn power # Return a closureraise_two =generate_power(2)# `generate_power(2)` is specific closureraise_three =generate_power(3)# `generate_power(3)` is specific closureraise_two(4)# 16raise_two(5)# 25raise_three(4)# 64raise_three(5)# 125for cell in raise_two.__closure__:print(cell.cell_contents)