魔法方法的“魔力”体现在它们总能够在适当的时候被自动调用
魔法方法的第一个参数应为cls(类方法) 或者self(实例方法)。
- cls:代表一个类的名称
- self:代表一个实例对象的名称## 构造与析构
__init__(self[,…])
- 构造器,当一个实例被创建的时候调用的初始化方法
__init__(self[,…])用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。
__init__() 方法可以包含多个参数,但必须包含一个名为 self 的参数,且必须作为第一个参数。也就是说,类的构造方法最少也要有一个 self 参数。
class Rectangle:
def __init__(self,x,y):
self.x=x
self.y=y
def getPeri(self):
return(self.x+self.y)*2
def getArea(self):
return self.x*self.y
a=Rectangle(5,4)
print(a.getArea()) #20
print(a.getPeri()) #18
注意 __init__()的返回值只能是None,不能是其他
class Rectangle:
def __init__(self,x,y):
return x
a=Rectangle(3,4) #TypeError: __init__() should return None, not 'int'
__new__(cls[,…])
__new__()才是在一个对象实例化的时候所调用的第一个方法。跟其他魔法方法不同的是,他的第一个参数是cls,
__new__()通常需要返回一个实例对象,通常是cls这个实例化对象,也可以是别的对象
除了继承一个不可变类型时重写一般使用他的默认方案
class Cap(str):
def __new__(cls, string):
string=string.upper()
return str.__new__(cls,string)
a=Cap('play')
print(a)#PLAY
__del__(self)
析构器,当一个对象将要被系统回收之时调用的方法。
Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;以此类推,
与 __init__() 方法对应的是 __del__() 方法,__init__() 方法用于初始化 Python 对象,而 __del__() 则用于销毁 Python 对象,即在任何 Python 对象将要被系统回收之时,系统都会自动调用该对象的 __del__() 方法。
当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收
class C:
def __init__(self):
print("abc")
def __del__(self):
print("嘎嘎嘎")
c1=C()
#abc
c2=c1
c3=c1
del c1
del c3
del c2
#嘎嘎嘎
#所有对象的引用被销毁时调用__del__()函数
__str__和__repr__
-
__str__(self):
-
当你打印一个对象的时候,触发__str__
-
当你使用%s格式化的时候,触发__str__
-
str强转数据类型的时候,触发__str__
-
__repr__(self):
-
_repr__是__str__的备胎
-
有__str__的时候执行__str__,没有实现__str__的时候,执行__repr__
-
__repr(obj)__内置函数对应的结果是__repr__的返回值
-
当你使用%r格式化的时候 触发__repr__
class Cat:
"""定义一个猫类"""
def __init__(self, new_name, new_age):
"""在创建完对象之后 会自动调用, 它完成对象的初始化的功能"""
self.name = new_name
self.age = new_age
def __repr__(self):
"""返回一个对象的描述信息"""
return "Cat:(%s,%d)" % (self.name, self.age)
def eat(self):
print("%s在吃鱼...." % self.name)
c=Cat("cc",10)
print(c)#Cat:(cc,10)
print(repr(tom)) # Cat:(汤姆,30)
#在class Cat中加上一个__str__(self)函数
def __str__(self):
"""返回一个对象的描述信息"""
return "名字是:%s , 年龄是:%d" % (self.name, self.age)
print(tom) # 名字是:汤姆 , 年龄是:30
__str__(self) 的返回结果可读性强
__repr__(self) 的返回结果应更准确
例如以下
import datetime
today = datetime.date.today()
print(str(today)) # 2019-10-11
print(repr(today)) # datetime.date(2019, 10, 11)
print('%s' %today) # 2019-10-11
print('%r' %today) # datetime.date(2019, 10, 11)
算术运算
工厂函数:类型是type类型,也就是类对象,、当调用时就是创建一个相应的实例对象。
python2.2后,对类和属性进行了统一,做法就是将int(),float(),str(),list(),tuple()这些内置函数转换为工厂函数
#创建的内置函数
print(type(len))#<class 'builtin_function_or_method'>
#工厂函数
print(type(int))#<class 'type'>
class C:
pass
print(type(C))#<class 'type'>
python的魔法方法提供了自定义对象的数值处理,通过以下魔法方法的重写,可以定义任何对象间的算术运算
算数操作符
class New_int(int):
#重新定义加法
def __add__(self, other):
return int.__mul__(self,other)
#重新定义减法
def __sub__(self, other):
return int.__floordiv__(self,other)
a=New_int(8)
b=New_int(8)
print(a+b)#64
如果不想调用python的默认方法,注意无限递归
class New_int(int):
def __add__(self, other):
return self-other
def __sub__(self, other):
return self+other
a=New_int(8)
b=New_int(8)
print(a+b)#RecursionError: maximum recursion depth exceeded
#交错性质的无限循环,调用加法里面调用减法,减法里面调用加法,陷入无限循环
class New_int(int):
def __add__(self, other):
return int(self)-int(other)
def __sub__(self, other):
return int(self)+(other)
a=New_int(8)
b=New_int(8)
print(a+b)#0
#加入int返回的就是整形的值
反运算
对于a+b
这里加数是a,被加数是b,因此是a主动,反运算就是a对象的__add__()方法没有实现或者不支持操作,python就会调用b的__radd__()方法
class Nint(int):
#在b的__radd(self,other)__,self是b对象,other是a对象
def __radd__(self, other):
return int.__sub__(other, self) # 注意 self 在后面,
a = Nint(5)
b = Nint(3)
print(a + b) # 8
#默认有__add__()所以b的__radd__()没有执行
print(1 + b) # -2
增量赋值运算符
魔法方法 | 含义 |
---|---|
iadd(self, other) | 定义赋值加法的行为:+= |
isub(self, other) | 定义赋值减法的行为:-= |
imul(self, other) | 定义赋值乘法的行为:*= |
itruediv(self, other) | 定义赋值真除法的行为:/= |
ifloordiv(self, other) | 定义赋值整数除法的行为://= |
imod(self, other) | 定义赋值取模算法的行为:%= |
ipow(self, other[, modulo]) | 定义赋值幂运算的行为:**= |
ilshift(self, other) | 定义赋值按位左移位的行为:<<= |
irshift(self, other) | 定义赋值按位右移位的行为:>>= |
iand(self, other) | 定义赋值按位与操作的行为:&= |
ixor(self, other) | 定义赋值按位异或操作的行为:^= |
ior(self, other) | 定义赋值按位或操作的行为: |
一元运算符
魔法方法 | 含义 |
---|---|
neg(self) | 定义正号的行为:+x |
pos(self) | 定义负号的行为:-x |
abs(self) | 定义当被abs()调用时的行为 |
invert(self) | 定义按位求反的行为:~x |
属性访问
魔法方法 | 含义 |
---|---|
getattr(self, name) | 定义当用户试图获取一个不存在的属性时的行为。 |
getattribute(self, name) | 定义当该类的属性被访问时的行为(先调用该方法,查看是否存在该属性,若不存在,接着去调用__getattr__)。 |
setattr(self, name, value) | 定义当一个属性被设置时的行为。 |
delattr(self, name) | 定义当一个属性被删除时的行为。 |
class C:
def __getattribute__(self, item):
print('__getattribute__')
#使用super()调用object基类的__getattribute__()方法
return super().__getattribute__(item)
def __getattr__(self, item):
print('__getattr__')
def __setattr__(self, key, value):
print('__setattr__')
super().__setattr__(key, value)
def __delattr__(self, item):
print('__delattr__')
super().__delattr__(item)
描述符(property原理)
描述符就是将某种特殊类型的类的实例指派给另一个类
特殊类型的类至少要在里面定义__get(),__set(),__delete()__三个特殊方法的任意一个
魔法方法 | 含义 |
---|---|
get(self, instance, owner) | 用于访问属性,它返回属性的值。 |
set(self, instance, value) | 将在属性分配操作中调用,不返回任何内容。 |
del(self, instance) | 控制删除操作,不返回任何内容。 |
class MyDecriptor:
def __get__(self, instance, owner):
print('__get__', self, instance, owner)
def __set__(self, instance, value):
print('__set__', self, instance, value)
def __delete__(self, instance):
print('__delete__', self, instance)
class Text:
x=MyDecriptor()
t=Text()
t.x
#访问x属性时候,自动调用__set()__方法,self是描述符类自身的实例;instance是这个描述符的拥有者所在的类的实例,在这里就是Text类的实例;owner是这个描述符的拥有者所在的类的本身
#__get__ <__main__.MyDecriptor object at 0x000001F45B50D688> <__main__.Text object at 0x000001F45B50D8C8> <class '__main__.Text'>
t.x = 'x-man'
#对x属性进行赋值操作时,python会自动调用__set()__方法,前两个参数跟__get()__方法是一样的,最后一个参数value是等号右边的值
#__set__ <__main__.MyDecriptor object at 0x000001C8D3CDD808> <__main__.Text object at 0x000001C8D3CDD908> x-man
#最后一个del操作也是同样的道理
del t.x
#__delete__ <__main__.MyDecriptor object at 0x000001C8D3CDD808> <__main__.Text object at 0x000001C8D3CDD908>
定制序列
协议(Protocols)与其它编程语言中的接口很相似,它规定你哪些方法必须要定义。然而,在 Python 中的协议就显得不那么正式。事实上,在 Python 中,协议更像是一种指南。
容器类型的协议
- 如果说你希望定制的容器是不可变的话,你只需要定义__len__()和__getitem()__方法。
- 如果你希望定制的容器是可变的话,除了__len__()和__getitem__()方法,你还需要定义__setitem__()和__delitem()__两个方法。
魔法方法 | 含义 |
---|---|
len(self) | 定义当被len()调用时的行为(返回容器中元素的个数)。 |
getitem(self, key) | 定义获取容器中元素的行为,相当于self[key]。 |
setitem(self, key, value) | 定义设置容器中指定元素的行为,相当于self[key] = value。 |
delitem(self, key) | 定义删除容器中指定元素的行为,相当于del self[key]。 |
编写一个不可改变的自定义列表,要求记录列表中每个元素被访问的次数。
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, item):
self.count[item] += 1
return self.values[item]
def __setitem__(self, key, value):
self.values[key] = value
def __delitem__(self, key):
del self.values[key]
for i in range(0, len(self.values)):
if i >= key:
self.count[i] = self.count[i + 1]
self.count.pop(len(self.values))
c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1]) # 3
print(c2[2]) # 6
c2[2] = 12
print(c1[1] + c2[2]) # 15
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
del c1[1]
print(c1.count)
# {0: 0, 1: 0, 2: 0, 3: 0}
迭代器
迭代的意思类似于循环,每一次重复的过程被称为一次迭代的过程,而每一次迭代得到的结果会被用来作为下一次迭代的初始值,提供迭代方法的容器称为迭代器,通常接触的迭代器序列(列表,元组,字符串)还有字典也是迭代器,都支持迭代操作
简单的例子
在这里插入代码片
- 迭代是 Python 最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。
- 迭代器只能往前不会后退。字符串,列表或元组对象都可用于创建迭代器
#for 语句迭代
a=range(1,10)
for i in a:
print(i,end=' ')#1 2 3 4 5 6 7 8 9
print(a)#range(1, 10)
迭代器有两个基本的内置函数:iter() 和 next()。
- iter(object) 函数用来生成迭代器。
- next(iterator[, default]) 返回迭代器的下一个项目。
- iterator – 可迭代对象
- default – 可选,用于设置在没有下一个元素时返回该默认值,如果不设置,又没有下一个元素则会触发 StopIteration异常。
str="laohu"
hu=iter(str)
print(next(hu))#l
print(next(hu))#a
print(next(hu))#o
print(next(hu))#h
print(next(hu))#u
print(next(hu))#StopIteration
#使用for循环
string="laohu"
str=iter(string)
while True:
try:
s=next(str)
except StopIteration:
break
print(s,end=" ")#l a o h u
迭代器基于以下两个方法实现:
- next(self):返回容器的下一个元素。
- iter(self):定义当迭代容器中的元素的行为,返回一个特殊的迭代器对象, 这个迭代器对象实现了 __next()__方法并通过 StopIteration 异常标识迭代的完成。
- StopIteration 异常用于标识迭代的完成,防止出现无限循环的情况,在 next() 方法中我们可以设置在完成指定循环次数后触发 StopIteration 异常来结束迭代。
class countDown:
def __init__(self,step):
self.step = step
def __next__(self):
if self.step <= 0:
raise StopIteration
self.step -= 1
return self.step
def __iter__(self):
return self
for element in countDown(10):
print(element,end=" ")#9 8 7 6 5 4 3 2 1 0
## 生成器 生成器也会提供 __next__() 方法,这意味着程序同样可调用内置的 next() 函数来获取生成器的下一个值,也可使用 for 循环来遍历生成器。
生成器与迭代器的区别在于,迭代器通常是先定义一个迭代器类,然后通过创建实例来创建迭代器;而生成器则是先定义一个包含 yield 语句的函数,然后通过调用该函数来创建生成器
- 创建生成器
创建生成器的两个步骤 - 定义一个包含 yield 语句的函数。
- 调用第 1 步创建的函数得到生成器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行
调用一个生成器函数,返回的是一个迭代器对象。
def test(val, step):
print("--------函数开始执行------")
cur = 0
# 遍历0~val
for i in range(val):
# cur添加i*step
cur += i * step
yield cur
上面函数与前面介绍的普通函数的最大区别在于 yield cur 这行,如果将这行代码改为 print(cur),那么这个函数就显得比较普通了,该函数只是简单地遍历区间,并将循环计数器乘以 step 后添加到 cur 变量上,该数列中两个值之间的差值会逐步递增。
yield cur 语句的作用有两点:
- 每次返回一个值,有点类似于 return 语句。
- 冻结执行,程序每次执行到 yield 语句时就会被暂停。
执行以下语句:
# 执行函数,返回生成器
t = test(10, 2)
print('=================')
# 获取生成器的第一个值
print(next(t)) # 0,生成器“冻结”在yield处
print(next(t)) # 2,生成器再次“冻结”在yield处
输出结果
=================
--------函数开始执行------
0
2
生成器是 Python 的一个特色功能,在其他语言中往往没有对应的机制,使用生成器至少有以下几个优势:
- 当使用生成器来生成多个数据时,程序是按需获取数据的,它不会一开始就把所有数据都生成出来,而是每次调用 next()
获取下一个数据时,生成器才会执行一次,因此可以减少代码的执行次数。比如前面介绍的示例,程序不会一开始就把生成器函数中的循环都执行完成,而是每次调用
next() 时才执行一次循环体。 - 当函数需要返回多个数据时,如果不使用生成器,程序就需要使用列表或元组来收集函数返回的多个值,当函数要返回的数据量较大时,这些列表、元组会带来一定的内存开销。如果使用生成器就不存在这个问题,生成器可以按需、逐个返回数据
- 使用生成器的代码更加简洁。
推导式
列表推导式(又称列表解析式)提供了一种简明扼要的方法来创建列表。
它的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是 0 个或多个 for 或者 if 语句。那个表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以 if 和 for 语句为上下文的表达式运行完成之后产生。
列表推导式的执行顺序:各语句之间是嵌套关系,左边第二个语句是最外层,依次往右进一层,左边第一条语句是最后一层。
[x*y for x in range(1,5) if x > 2 for y in range(1,4) if y < 3]
执行顺序
for x in range(1,5)
if x > 2
for y in range(1,4)
if y < 3
x*y
列表推导式
a = [i for i in range(100) if (i % 2) != 0 and (i % 3) == 0]
print(a)
# [3, 9, 15, 21, 27, 33, 39, 45, 51, 57, 63, 69, 75, 81, 87, 93, 99]
字典推导式
b = {i: i % 2 == 0 for i in range(10) if i % 3 == 0}
print(b)
# {0: True, 3: False, 6: True, 9: False}
集合推导式
c = {i for i in [1, 2, 3, 4, 5, 5, 6, 4, 3, 2, 1]}
print(c)
# {1, 2, 3, 4, 5, 6}
其它
d = 'i for i in "I Love Lsgogroup"'
print(d)
# i for i in "I Love Lsgogroup"
e = (i for i in range(10))
print(e)
# <generator object <genexpr> at 0x0000007A0B8D01B0>
#generator生成器,表明,用普通小括号括起来的就是生成器推导式
print(next(e)) # 0
print(next(e)) # 1
for each in e:
print(each, end=' ')
# 2 3 4 5 6 7 8 9
#生成器推导式如果作为函数的参数,可以直接写推导式不用加小括号
s = sum([i for i in range(101)])
print(s) # 5050