魔法方法
1.总是被双下划线包围
2.是面向对象的Python的一切
3.总是能在适当时候被自动调用
构造函数
init(self,param1,param2…)
当创建一个对象的时候,init函数就会被自动调用,类似于C++的构造函数。
>>> class ball:
def __init__(self,name):
self.name = name
def kick(self):
print('我叫%s,该死的,谁踢我...'% self.name)
>>> b = ball('土豆')
>>> b.kick()
我叫土豆,该死的,谁踢我...
>>> c = ball()
Traceback (most recent call last):
File "<pyshell#55>", line 1, in <module>
c = ball()
TypeError: __init__() missing 1 required positional argument: 'name'
构造函数不能做返回。
>>> class A:
def __init__(self):
return "A of A-cup"
>>> a = A()
Traceback (most recent call last):
File "<pyshell#186>", line 1, in <module>
a = A()
TypeError: __init__() should return None, not 'str'
事实上,new(cls[,…])才是定义一个对象时最先被调用的,一般都是不去做重写,用Python自己的方案调用。当类继承一个不可变类型又需要修改时就需要使用它。
>>> class CapStr(str):
def __new__(cls,string):
string = string.upper()
return str.__new__(cls,string)
>>> a = CapStr("I love FishC.com!")
>>> a
'I LOVE FISHC.COM!'
当创建a时首先会调用CapStr里的new函数,这时会执行重写的new函数,然后再返回基类str的new函数,以此来解决当类继承一个不可变类型而又要修改它时的问题。
析构函数
del(self)
当垃圾回收机制调用时,才会调用析构函数。
>>> class C:
def __init__(self):
print("我是__init__方法,我被调用了...")
def __del__(self):
print("我是__del__方法,我被调用了...")
>>> c1 = C()
我是__init__方法,我被调用了...
>>> c2 = c1
>>> c3 = c2
>>> del c1
>>> del c2
>>> del c3
我是__del__方法,我被调用了...```
**算术运算**
Python无处不对象。
```python
>>> type(len)
<class 'builtin_function_or_method'>
>>> type(dir)
<class 'builtin_function_or_method'>
>>> type(int)
<class 'type'>
>>> type(list)
<class 'type'>
>>> class C:
pass
>>> type(C)
<class 'type'>
>>> a = int('123')
>>> a
123
>>> b = int('456')
>>> a + b
579
诸如len、dir、int、list这些工厂函数其本质上还是类对象,他们会返回一个实例对象,如a和b就是类对象int返回的实例对象,而对象之间又可以进行算术运算,其实两个对象之间的运算属于魔法方法,其本质上就是调用不同的魔法方法实现对象之间的运算。
而我们可以通过重新,来满足更多的需求。
>>> class New_int(int):
def __add__(self, other):
return int.__sub__(self, other)
def __sub__(self,other):
return int.__add__(self,other)
>>> a = New_int(3)
>>> b = New_int(5)
>>> a + b
-2
>>> a - b
8
上式则是通过创建一个继承于int的类对象New_int,然后对其的__add__和__sub__进行改写。
但是改写时一定要注意,很容易会出现无限递归的情况。
>>> class New_int(int):
def __add__(self, other):
return self + other
def __sub__(self,other):
return self - other
>>> a = New_int(3)
>>> b = New_int(5)
>>> a + b
Traceback (most recent call last):
File "<pyshell#232>", line 1, in <module>
a + b
File "<pyshell#229>", line 3, in __add__
return self + other
File "<pyshell#229>", line 3, in __add__
return self + other
File "<pyshell#229>", line 3, in __add__
return self + other
[Previous line repeated 1022 more times]
RecursionError: maximum recursion depth exceeded
由于self + other这一行里的+,程序无限循环调用__add__函数。
算术运算参考表
其中divmod是返回两个数进行地板除法后,剩下的余数。
其中重写反运算方法时,要注意它的顺序问题。
>>> class Nint(int):
def __radd__(self,other):
return int.__sub__(self,other)
>>> a = Nint(5)
>>> b = Nint(3)
>>> a + b
8
>>> 1 + b
2
上面的a + b由于a里由__add__函数,所以会优先执行它,而1 + b,因为1不是实例对象,没有__add__函数,所以会调用b中的__radd__函数进行反运算,由于__radd__函数中的self是b,other是1,所以1 + b即3 - 1运算出来的结果是2.
属性访问
常用的一些属性访问BIF。
我们通过重写这些魔法方法来讲解他们的用法。
>>> class C:
def __getattribute__(self, name):
print("getattribute")
return super().__getattribute__(name)
def __getattr__(self, name):
print("getattr")
def __setattr__(self, name, value):
print("setattr")
super().__setattr__(name, value)
def __delattr__(self, name):
print("delattr")
super().__delattr__(name)
>>> c = C()
>>> c.x
getattribute
getattr
>>> c.x = 1
setattr
>>> c.x
getattribute
1
>>> del c.x
delattr
当c.x这个属性不存在时,系统会先调用getattribute方法再调用getattr方法,当c.x被赋值为1后,再次访问,系统只调用getattribute方法,所以当访问某个属性时,若属性不存在则系统会再调用一次getattr方法。
改写属性访问BIF时要注意循环陷阱。
class Rectangle:
def __init__(self, width=0, hight=0):
self.width = width
self.hight = hight
def __setattr__(self, name, value):
if name == 'square':
self.width = value
self.hight = value
else:
self.name = value
def getArea(self):
return self.width * self.hight
输出结果:系统陷入无限递归,达到最大递归深度后,系统报错。
>>> r = Rectangle(4,5)
Traceback (most recent call last):
File "<pyshell#76>", line 1, in <module>
r = Rectangle(4,5)
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 3, in __init__
self.width = width
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 10, in __setattr__
self.name = value
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 10, in __setattr__
self.name = value
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 10, in __setattr__
self.name = value
[Previous line repeated 1019 more times]
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 6, in __setattr__
if name == 'square':
RecursionError: maximum recursion depth exceeded in comparison
>>>
当执行self.width = width时,会调用__setattr__,之后程序走else部分,即执行self.name = value,此时会再次调用__setattr__,然后系统就陷入了无限递归。
解决这种问题我们可以用super函数调用父类的__setattr__或用__dict__字典方式赋值.
class Rectangle:
def __init__(self, width=0, hight=0):
self.width = width
self.hight = hight
def __setattr__(self, name, value):
if name == 'square':
self.width = value
self.hight = value
else:
self.__dict__[name] = value #super().__setattr__(name, value)
def getArea(self):
return self.width * self.hight
输出结果
>>> r = Rectangle(4,5)
>>> r.width
4
>>> r.hight
5
>>> r.getArea()
20
>>> r.square = 10
>>> r.width
10
>>> r.hight
10
>>> r.getArea()
100
在重写属性访问BIF时一定要注意递归陷阱!!!
描述符
描述符就是将某种特殊类型的类的实例指派给另一个类的属性。
特殊类型的要求就是含有__get__、set、__delete__这三个方法。
现在我们通过例子来看下描述符。
>>> class MyDecriptor:
def __get__(self, instance, owner):
print("getting...",self, instance, owner)
def __set__(self, instance, owner):
print("setting...",self, instance, owner)
def __delete__(self, instance):
print("deleting...",self, instance)
>>> class Test:
x = MyDecriptor()
>>> test = Test()
>>> test.x
getting... <__main__.MyDecriptor object at 0x0000028E61C1C940> <__main__.Test object at 0x0000028E63CACA30> <class '__main__.Test'>
>>> test
<__main__.Test object at 0x0000028E63CACA30>
>>> Test
<class '__main__.Test'>
>>> test.x = "X-man"
setting... <__main__.MyDecriptor object at 0x0000028E61C1C940> <__main__.Test object at 0x0000028E63CACA30> X-man
>>> del test.x
deleting... <__main__.MyDecriptor object at 0x0000028E61C1C940> <__main__.Test object at 0x0000028E63CACA30>
由其中可以知道,我们定义了一个描述符类,并将这个描述符类指派给了Test类的x属性,其中描述符类里的self指属性test.x,instance指实例对象test,owner指类对象Test,这就是一个描述符的基本用法,从这可以知道property方法其本质上也是定义一个描述符类,因此我们可以定义属于我们自己的property。
class MyProperty:
def __init__(self , fget=None, fset=None, fdel=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
def __get__(self, instance, owner):
return self.fget(instance)
def __set__(self, instance, value):
self.fset(instance, value)
def __delete__(self, instance):
self.fdel(in
> 这里是引用
stance)
class C:
def __init__(self):
self._x = None
def getX(self):
return self._x
def setX(self, value):
self._x = value
def delX(self):
del self._x
x = MyProperty(getX, setX, delX)
>>> c = C()
>>> c.x = 'X-man'
>>> c.x
'X-man'
>>> c._x
'X-man'
>>> del c.x
>>> c.x
Traceback (most recent call last):
File "<pyshell#176>", line 1, in <module>
c.x
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 7, in __get__
return self.fget(instance)
File "F:\小甲鱼学习记录\小甲鱼Python课后作业\test.py", line 17, in getX
return self._x
AttributeError: 'C' object has no attribute '_x'
上面的MyProperty含有__get__、__set__和__delete__这三个函数,因此它是特殊类型,我们可以将它指派给C类的x属性。然后我们又定义了一个C类,并且将定义后的getX、setX和delX传入我们自定义的描述符MyProperty,再指派给x。
下面是一个练习
class Celsuis:
def __init__(self, value=26.0):
self.value = value
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Fahrenheit:
def __get__(self, instance, owner):
return instance.cel*1.8+32
def __set__(self, instance, value):
instance.cel = (float(value) - 32)/1.8
class Temperature:
cel = Celsuis()
fah = Fahrenheit()
输出结果
>>> temp = Temperature()
>>> temp.cel
26.0
>>> temp.cel = 30
>>>> temp.cel
30.0
先创建一个实例对象temp,然后执行temp.cel,这里的cel是实例对象temp的属性同时它也是一个类对象Celsuis的实例对象,之后调用Celsuis中的__get__方法,结果显示为26.0。然后输入temp.cel = 30,系统调用Celsuis中的__set__方法,再执行temp.cel。
>>> temp.fah
86.0
>>> temp.fah = 100
>>> temp.cel
37.77777777777778
之后输入temp.fah,同理tenp.cel,系统调用Fahrenheit中的__get__方法返回instance.cel*1.8+32 其中instance指实例对象temp,此时系统会再调用Celsuis中的__get__方法,输出结果86.0。输入temp.fah,系统调用Fahrenheit中的__set__方法执行instance.cel = (fioat(value) - 32)/1.8,此时系统会再调用Celsuis中的__set__方法。
描述符的定义需要定义在类的层次上,若定义在实例层次上,系统将无法自动调用__get__、__set__和__delete__方法。
>>> class MyDes:
def __init__(self, value = None):
self.val = value
def __get__(self, instance, owner):
return self.val ** 2
>>> class Test:
def __init__(self):
self.x = MyDes(3)
>>> test = Test()
>>> test.x
输出结果
>>> test.x
<__main__.MyDes object at 0x1058e6f60>
定制序列
我们可以基于序列的协议形式,通过改写里面的魔法方法来定制属于我们自己的容器。
class CountList:
def __init__(self, *args):
self.values = [x for x in args]
self.count = {}.fromkeys(range(len(self.values)), 0)
def __len__(self):
return len(self.values)
def __getitem__(self, key):
self.count[key] += 1
return self.values[key]
>>> c1 = CountList(1,3,5,7,9)
>>> c2 = CountList(2,4,6,8,10)
>>> c1[1]
3
>>> c2[1]
4
>>> c1.count
{0: 0, 1: 1, 2: 0, 3: 0, 4: 0}
>>> c1[1]
3
>>> c1.count
{0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
上面我们定义了一个不可改变的列表,并且能够统计访问的次数。
迭代器
for i in "FishC":
print(i)
F
i
s
h
C
里面的字符串是容器,也是迭代器,而for语句是迭代器的触发
而Python提供了两个迭代的BIF:iter()和next()
>>> string = "FishC"
>>> it = iter(string)
>>> next(it)
'F'
>>> next(it)
'i'
>>> next(it)
's'
>>> next(it)
'h'
>>> next(it)
'C'
>>> next(it)
Traceback (most recent call last):
File "<pyshell#97>", line 1, in <module>
next(it)
StopIteration
iter()的作用是生成一个迭代器,而next()则是一个触发条件。
他们相对应的魔法方法是__iter__()和__next__()
我们通过改写来实现斐波那契数列。
class Fibs:
def __init__(self, n=20):
self.a = 0
self.b = 0
self.n = n
def __iter__(self):
return self
def __next__(self):
self.a, self.b = self.b, self.a+self.b
if self.a>self.n:
raise StopIteration
return self.a
>>> fibs = Fibs()
>>> for each in fibs:
print(each)
1
1
2
3
5
8
13