本文目录一览:
- 1、详解Python中的__new__、__init__、__call__三个特殊方法
- 2、Python中常见魔法方法介绍
- 3、python中什么是特殊方法
- 4、python元编程中一些处理属性的特殊方法
详解Python中的__new__、__init__、__call__三个特殊方法
__new__: 对象的创建,是一个静态方法,第一个参数是cls。(想想也是,不可能是self,对象还没创建,哪来的self)
__init__ : 对象的初始化, 是一个实例方法,第一个参数是self。
__call__ : 对象可call,注意不是类,是对象。
先有创建,才有初始化。即先__new__,而后__init__。
上面说的不好理解,看例子。
1.对于__new__
可以看到,输出来是一个Bar对象。
__new__方法在类定义中不是必须写的,如果没定义,默认会调用object.__new__去创建一个对象。如果定义了,就是override,可以custom创建对象的行为。
聪明的读者可能想到,既然__new__可以custom对象的创建,那我在这里做一下手脚,每次创建对象都返回同一个,那不就是单例模式了吗?没错,就是这样。可以观摩《飘逸的python - 单例模式乱弹》
定义单例模式时,因为自定义的__new__重载了父类的__new__,所以要自己显式调用父类的__new__,即object.__new__(cls, *args, **kwargs),或者用super()。,不然就不是extend原来的实例了,而是替换原来的实例。
2.对于__init__
使用Python写过面向对象的代码的同学,可能对 __init__ 方法已经非常熟悉了,__init__ 方法通常用在初始化一个类实例的时候。例如:
这样便是__init__最普通的用法了。但__init__其实不是实例化一个类的时候第一个被调用 的方法。当使用 Persion(name, age) 这样的表达式来实例化一个类时,最先被调用的方法 其实是 __new__ 方法。
3.对于__call__
对象通过提供__call__(slef, [,*args [,**kwargs]])方法可以模拟函数的行为,如果一个对象x提供了该方法,就可以像函数一样使用它,也就是说x(arg1, arg2...) 等同于调用x.__call__(self, arg1, arg2) 。模拟函数的对象可以用于创建防函数(functor) 或代理(proxy).
总结,在Python中,类的行为就是这样,__new__、__init__、__call__等方法不是必须写的,会默认调用,如果自己定义了,就是override,可以custom。既然override了,通常也会显式调用进行补偿以达到extend的目的。
这也是为什么会出现"明明定义def _init__(self, *args, **kwargs),对象怎么不进行初始化"这种看起来诡异的行为。(注,这里_init__少写了个下划线,因为__init__不是必须写的,所以这里不会报错,而是当做一个新的方法_init__)
Python中常见魔法方法介绍
魔法方法 (Magic Methods) 是Python中的内置函数,一般以双下划线开头和结尾,例如__ init__ 、 __del__ 等。之所以称之为魔法方法,是因为这些方法会在进行特定的操作时会自动被调用。
在Python中,可以通过dir()方法来查看某个对象的所有方法和属性,其中双下划线开头和结尾的就是该对象的魔法方法。以字符串对象为例:
可以看到字符串对象有 __add__ 方法,所以在Python中可以直接对字符串对象使用"+"操作,当Python识别到"+"操作时,就会调用该对象的 __add__ 方法。有需要时我们可以在自己的类中重写 __add__ 方法来完成自己想要的效果。
我们重写了 __add__ 方法,当Python识别"+"操作时,会自动调用重写后的 __add__ 方法。可以看到,魔法方法在类或对象的某些事件出发后会自动执行,如果希望根据自己的程序定制特殊功能的类,那么就需要对这些方法进行重写。使用魔法方法,我们可以非常方便地给类添加特殊的功能。
1、构造与初始化
__ new __ 、 __ init __ 这两个魔法方法常用于对类的初始化操作。上面我们创建a1 = A("hello")时,但首先调用的是 __ new __ ;初始化一个类分为两步:
a.调用该类的new方法,返回该类的实例对象
b.调用该类的init方法,对实例对象进行初始化。
__new__ (cls, *args, **kwargs)至少需要一个cls参数,代表传入的类。后面两个参数传递给 __ init __ 。在 __ new __ 可以决定是否继续调用 __ init __ 方法,只有当 __ new __ 返回了当前类cls的实例,才会接着调用 __ init __ 。结合 __ new __ 方法的特性,我们可以通过重写 __ new __ 方法实现Python的单例模式:
可以看到虽然创建了两个对象,但两个对象的地址相同。
2、控制属性访问这类魔法
方法主要对对象的属性进行访问、定义、修改时起作用。主要有:
__getattr__(self, name): 定义当用户试图获取一个属性时的行为。
__getattribute__(self, name):定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用getattr)。
__setattr__(self, name, value):定义当一个属性被设置时的行为。
当初始化属性时如self.a=a时或修改实例属性如ins.a=1时本质时调用魔法方法self. __ setattr __ (name,values);当实例访问某个属性如ins.a本质是调用魔法方法a. __ getattr __ (name)
3、容器类操作
有一些方法可以让我们自己定义自己的容器,就像Python内置的List,Tuple,Dict等等;容器分为可变容器和不可变容器。
如果自定义一个不可变容器的话,只能定义__ len__ 和__ getitem__ ;定义一个可变容器除了不可变容器的所有魔法方法,还需要定义__ setitem__ 和__ delitem__ ;如果容器可迭代。还需要定义__ iter __。
__len__(self):返回容器的长度
__getitem__(self,key):当需要执行self[key]的方式去调用容器中的对象,调用的是该方法
__setitem__(self,key,value):当需要执行self[key] = value时,调用的是该方法
__iter__(self):当容器可以执行 for x in container:,或者使用iter(container)时,需要定义该方法
下面举一个例子,实现一个容器,该容器有List的一般功能,同时增加一些其它功能如访问第一个元素,最后一个元素,记录每个元素被访问的次数等。
这类方法的使用场景主要在你需要定义一个满足需求的容器类数据结构时会用到,比如可以尝试自定义实现树结构、链表等数据结构(在collections中均已有),或者项目中需要定制的一些容器类型。
魔法方法在Python代码中能够简化代码,提高代码可读性,在常见的Python第三方库中可以看到很多对于魔法方法的运用。
因此当前这篇文章仅是抛砖引玉,真正的使用需要在开源的优秀源码中以及自身的工程实践中不断加深理解并合适应用。
python中什么是特殊方法
构造序列
1._len_(self)
2._getitem_(self,key)
3._setitem_(self,key,value)
4._delitem_(self,key)
程序演示:
myseq.py
class MySeq:
def __init__(self):
self.lseq = ["I","II","III","IV"]
def __len__(self):
return len(self.lseq)
def __getitem__(self,key):
if 0 = key 4:
return self.lseq[key]
if __name__ == '__main__':
m = MySeq()
for i in range(4):
print(m[i])
python元编程中一些处理属性的特殊方法
在用户自己定义的类中,下述特殊方法用于获取、设置、删除和列出属性
使用点号,或者内置的的getattr,hasattr, setattr函数存取属性都会触发下述列表中的特殊方法。但是,直接通过实例的 __dict__ 属性来存取方法不会触发这些特殊方法。所以,如果需要,在一些情况下,可以使用这种方法跳过特殊方法。
示例,假设有个名为 Class 的类,obj 是 Class 类的实例,attr 是 obj 的属性。
obj.attr 和 getattr(obj, 'attr', 42)都会触发 Class.__getattribute__(obj, 'attr') 方法。尝试获取指定属性时总会调用这个方法,不过,寻找的属性时特殊属性或者特殊方法时除外。
del obj.attr 语句会触发 Class.__delattr__(obj, 'attr') 方法。
dir(obj)语句会触发 Class.__dir__(obj) 方法。
仅当获取指定的属性失败,搜索过 obj、Class 和超类之后调用 Class.__getattr__(self, name)
尝试设置指定的属性时总会调用 Class.__setattr__(obj, 'attr', value) ,点号和setattr(obj, 'attr', 42)会调用这个方法。