目录
1. __init__()方法:构造器,当一个实例对象被定义时调用
3. __del__()方法:析构器,当删除一个实例对象时调用
https://www.cnblogs.com/Jimmy1988/p/6801795.html
一、构造和析构
1. __init__()方法:构造器,当一个实例对象被定义时调用
(1)对象初始化的方法,只有需要初始化的时候才重写该方法
(2)括号内默认参数 self 为 Python 自动添加,其为 __new__() 返回的实例
(3)返回值为 None,即不需要返回值,不能是其他
>>> class Rectangular:
... 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
>>> rect = Rectangular(3, 4)
>>> rect.getPerl()
14
>>> rect.getArea()
12
2. __new__()方法
(1)对象实例化时嗲用的首个方法
(2)括号内至少有一个参数 cls,代表实例化的类
(3)需要返回值,通常是 类实例化的对象,也可以返回其他对象
(4)当类需要继承一个不可变的类型时,需要重新该方法
>>> class CapStr(str):
... def __new__(cls, string):
... string = string.upper()
... return str.__new__(cls, string)
>>> a = CapStr("I can not quite understand the method")
>>> a
'I CAN NOT QUITE UNDERSTAND THE METHOD'
3. __del__()方法:析构器,当删除一个实例对象时调用
(1)不同于上述两个构造方法构造当前类的实例对象,__del__() 方法用来销毁实例化对象
(2)当一个对象的引用计数为 0 的时候会自动调用 __del__() 方法释放这个对象占用的内存
>>> import sys
>>> class C:
... def __init__(self):
... print("调用了 __init__() 方法")
... def __del__(self):
... print("调用了 __del__() 方法")
>>> c1 = C()
'调用了 __init__() 方法'
>>> sys.getrefcount(c1)
2 # 为什么显示引用了2次
>>> c2 = c1
>>> c3 = c2
>>> del c1
>>> del c2
>>> del c3
'调用了 __del__() 方法'
(魔法方法汇总可点击链接)
https://www.cnblogs.com/Jimmy1988/p/6801795.html
二、算术运算
1. 工厂函数:即类对象,调用时会创建一个相应的实例对象
# int()、 float()、 str()、 list()、 tuple() 等 BIF 在 Python2.2 后转换为 工厂函数
# 类型同“类”一样。
>>> type(len)
<class 'builtin_function_or_method'>
>>> type(int)
<class 'type'>
>>> class C:
... pass
>>> type(C)
<class 'type'>
>>> a = int('123')
>>> a
123
>>> b = int('456')
>>> a + b
579
2. 常见算术运算
举例:
# 常规写法
>>> class New_int(int):
... def __add__(self, other):
... return int.__add__(self, other)
... def __sub__(self, other):
... return int.__sub__(self, other)
>>> a = New_int(2)
>>> b = New_int(3)
>>> a + b
5
>>> a - b
-1
# 自定义时需注意返回值
>>> class Test_int(int):
... def __add__(self, other):
# 该写法会导致递归,
# 即 返回对象 + 另一个对象 将重新调用 __add__(self, other) 方法
... return self + other
... def __sub__(self, other):
... return self - other
>>> a = Test_int(2)
>>> b = Test_int(3)
>>> a + b
Traceback (most recent call last):
File "<pyshell#64>", line 1, in <module>
a + b
File "<pyshell#61>", line 3, in __add__
return self + other
File "<pyshell#61>", line 3, in __add__
return self + other
File "<pyshell#61>", line 3, in __add__
return self + other
[Previous line repeated 1022 more times]
RecursionError: maximum recursion depth exceeded
# 可改写成此种形式
>>> class Test_int(int):
... def __add__(self, other):
... return int(self) + int(other)
... def __sub__(self, other):
... return int(self) - int(other)
>>> a = Test_int(2)
>>> b = Test_int(3)
>>> a + b
5
3. 反运算
例如:
>>> class Nint(int):
... def __radd__(self, other):
... # 注重操作数顺序的运算符(如:减法、除法 和 位移 等),重写反运算魔法方法时要注意顺序
... return int.__sub__(other, self) # 注:other 和 self 不能写反
>>> a = Nint(2)
>>> b = Nint(3)
# 这里加数是 a,被加数是 b,因此是 a 主动
# 对于 a+b,b 的 __radd__(self,other) 的 self 是 b 的对象,other 是 a 的对象
>>> a + b
5
# 反运算:如果 a 对象的 __add__() 方法没有实现或者不支持相应的操作
# 那么 Python 就会调用 b 的 __radd__() 方法
>>> 1 + b
-2
4. 一元操作符
(1)__neg__():一元取负算术运算符
(2)__pos__():一元取正算术运算符
(3)__abs__():返回数字绝对值
(4)__invert__():整数按位取反,定义为 ~x == -(x+1)
>>> a = 123
>>> a.__invert__()
-124
三、小练习:计时器
要求:(1)创建一个代表计时器的类
(2)定义两种方法,分别表示计时开始和结束
(3)输入实例化对象 t1 或 print(t1) 都能正确显示结果
(4)当未运行计时器或终止计时器时,会有人性化提醒
(5)两个计时器的实例化对象可相加
(6)计时器只考虑到秒
>>> import time as tm
>>> class MyTimer:
... # 重写 __init__() 方法,保证未采用 MyTimer 内部的方法时,程序有提醒
... def __init__(self):
... # 指定计时器的单位
... self.unit = ['年', '月', '天', '时', '分', '秒']
... # 指定计时器的单位进制
... self.borrow = [0, 12, 31, 24, 60, 60]
... # 设定未使用内部方法时的提醒信息
... self.prompt = "MyTimer is not running."
... # 初始化计时器的持续运行时间
... self.lasted = []
... # 若采用 self.start = 0 ,则属性和方法同名,此时属性会覆盖方法
... self.begin = 0
... self.end = 0
...
... # 若添加 __repr__ = __str__ 代码,则只输入实例化对象时,不再会显示对象的内存地址
... # 也可直接采用 def __repr__(self): return self.prompt 的方法
... def __str__(self):
... return self.prompt
... __repr__ = __str__
...
... # 设计两个计时器对象可以相加
... def __add__(self, other):
... result = []
... prompt = "Running time: "
... for i in range(6):
... result.append(self.lasted[i] + other.lasted[i])
... if result[i]:
... prompt += (str(result[i]) + self.unit[i])
... return prompt
...
... # 计时器开始
... def start(self):
... # 采用 time 模块的 localtime() 函数,返回一个时间元组,只保留前6个元素
... self.begin = tm.localtime()
... # 若调用 start 方法后未调用 stop 方法,则输出当前 self.prompt 的内容
... self.prompt = "Please call method 'stop'."
... print("MyTimer running...")
...
... # 计时器终止
... def stop(self):
... # 若先调用 stop 方法,未调用 start 方法,则输出提醒信息
... if not self.begin:
... print("Please call method 'start' first.")
... else:
... self.end = tm.localtime()
... self._calc()
... print("End of MyTimer...")
...
... # 内部计算运行时间
... def _calc(self):
... self.lasted = []
... self.prompt = "Running time: "
... for i in range(6):
... self.lasted.append(self.end[i] - self.begin[i])
... if self.lasted[i]:
... self.prompt += (str(self.lasted[i]) + self.unit[i])
...
... print(self.prompt)
...
... # 为下一轮计时初始化变量
... self.begin = 0
... self.end = 0
四、属性访问
属性在哪个对象上定义,便会出现在哪个对象的 __dict__ 中。
>>> class Test1:
... a = 1
>>> class Test2(Test1):
... b = 2
... def __init__(self, num):
... self.num = num
... def test(self):
... return self.num + 1
>>> t2 - Test2(2)
>>> t2.__dict__
{'num': 2}
>>> Test2.__dict__
mappingproxy({'__module__': '__main__', 'b': 2, '__init__': <function Test2.__init__ at 0x000002094F0F4700>, 'test': <function Test2.test at 0x000002094F1054C0>, '__doc__': None})
>>> t2.test()
3
>>> Test1.__dict__
mappingproxy({'__module__': '__main__', 'a': 1, '__dict__': <attribute '__dict__' of 'Test1' objects>, '__weakref__': <attribute '__weakref__' of 'Test1' objects>, '__doc__': None})
Python 中常用点操作符来访问对象的属性,也可通过魔法方法来对属性进行管理。
__getattribute__() 方法是实例对象查找属性或方法的入口。实例对象访问属性或方法时都需要调用该方法,之后才会根据一定的规则在各个 __dict__() 中查找相应的属性值或方法对象,若没有找到则会调用 __getattr__() 方法。
例如:
>>> class C:
... def __getattribute__(self, name):
... print('===getattribute===')
... return super().__getattribute__(name)
... def __setattr__(self, name, value):
... print('===setattr===')
... super().__setattr__(name, value)
... def __delattr__(self, name):
... print('===delattr===')
... super().__delattr__(name)
... def __getattr__(self, name):
... print('===getattr===')
...
>>> c = C()
# 输出的结果不正确,为什么?
小甲鱼运行结果:
小练习:测BMI指数
>>> class Carl_BMI:
... # 体重单位:kg; 身高单位:m
... def __init__(self, weight = 0, height = 0):
... self.weight = weight
... self.height = height
...
... def __setattr__(self, name, value):
... # 只有为叫 “Giant” 的属性赋值才会采用 if 下的语句
... if name == 'Giant':
... self.weight = value
... self.height = value
... else:
... # __dict__ 方法 和 super() 方法可二选一
... # self.__dict__[name] = value
... super().__setattr__(name, value)
...
... def __getBMI__(self):
... return self.weight / ( self.height ** 2 )
>>> c = Carl_BMI(65, 1.77)
>>> c.__getBMI__()
20.747550193111813
>>> c.Giant = 10
>>> c.__getBMI__()
0.1
>>> c.Lily = 20
>>> c.__getBMI__()
0.1
>>> c = Carl_BMI()
>>> c.Lily = 20
>>> c.__getBMI__()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "F:/PyCharm_Files/Carl_BMI.py", line 16, in __getBMI__
return self.weight / ( self.height ** 2 )
ZeroDivisionError: division by zero
>>> c.Giant = 20
>>> c.__getBMI__()
0.05
(详情参考: Python中的属性访问与描述符 - 阿谋 - 博客园)
五、描述符
描述符(descriptor):将某种特殊类型的类(该类至少定义 __get__() 、__set__() 和 __delete__() 三个方法之一)的实例指派给另一个类的属性。
描述符相关魔法方法:
例如:
# 定义描述符类 TestDescriptor
# 其实现了 __get__()、__set__() 和 __del__() 方法
>>> class TestDescriptor:
... # self 表示:描述符类自身的实例;
... # instance 表示:描述符拥有者所在的类的实例,此处为 Test 类的实例;
... # owner 表示该描述符拥有者所在的类本身,此处为 Test 类
... def __get__(self, instance, owner):
... print("getting...", self, instance, owner)
... # 对 x 属性赋值则自动调用 __set__() 方法,self 和 instance 同 __get__() 方法,value 为所赋的值
... def __set__(self, instance, value):
... print("setting...", self, instance, value)
... def __delete__(self, instance):
... print("deleting...", self, instance)
>>> class Test:
... # 将描述符类 TestDescriptor 的类实例指派给 Test 的类属性
... x = TestDescriptor()
>>> test = Test()
>>> test.x
getting... <__main__.TestDescriptor object at 0x0000025CE815FF10> <__main__.Test object at 0x0000025CE82B0C10> <class '__main__.Test'>
>>> test.x = "Hahaha"
setting... <__main__.TestDescriptor object at 0x0000025CE815FF10> <__main__.Test object at 0x0000025CE82B0C10> Hahaha
>>> del test.x
deleting... <__main__.TestDescriptor object at 0x0000025CE83396D0> <__main__.Test object at 0x0000025CE8339940>
自定义描述符类
>>> class MyProperty:
... def __init__(self, fget, fset, fdel):
... 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(instance)
>>> class C:
... def __init__(self):
... self._x = None
... def get_x(self):
... return self._x
... def set_x(self, value):
... self._x = value
... def del_x(self):
... del self._x
...
... x = MyProperty(get_x, set_x, del_x)
>>> c = C()
>>> c.x = 'a'
>>> c._x
'a'
小练习:一个温度类 + 两个分别描述摄氏度和华氏度属性的描述类,两属性自动转换
>>> class Celsius:
... # 此处的 __init__() 方法不重写也可正常运行,可以删除?
... def __init__(self, value = 0):
... self.value = float(value)
... def __get__(self, instance, owner):
... return self.value
... def __set__(self, instance, value):
... self.value = float(value)
... def __delete__(self, instance):
... del self.value
>>> class Fahrenheit:
... def __get__(self, instance, owner):
... # instance.cel 为摄氏度的值,返回华氏度的值
... return instance.cel * 1.8 +32
... def __set__(self, instance, value):
... # 通过设置华氏度的值,返回摄氏度的值
... instance.cel = (float(value) - 32) /1.8
>>> class Temperature:
... cel = Celsius()
... fah = Fahrenheit()
六、定制容器
定制容器不可变:只定义 __len__() 和 __getitem__() 方法
定制容器可变:还需定义 __setitem__() 和 __delitem__() 方法
鸭子类型:
鸭子类型(duck typing),《零基础入门学习Python》,Python交流,鱼C论坛 - Powered by Discuz!
定制容器相关的魔法方法:
小练习:(1)自定义一个只读列表;(2)记录每个元素的被访问次数
>>> class MyList:
... def __init__(self, *args):
... self.values = [x for x in args]
... # Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值
... # dict.fromkeys(seq[, value])
... 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 = MyList(1, 2, 4, 4, 5)
>>> c2 = MyList(5, 10, 15, 20, 25)
>>> c1[1]
2
>>> c2[2]
15
>>> c1.count
{0: 0, 1: 1, 2: 0, 3: 0, 4: 0}
>>> c2.count
{0: 0, 1: 0, 2: 1, 3: 0, 4: 0}
七、迭代器 和 生成器
1. 迭代器:迭代类似于循环,每次重复都相当于一次迭代,而每次迭代的结果都用作下一次迭代的初始值;迭代器序列通常为序列和字典等。
BIF:(1)iter():调用该方法则得到某一容器对象的迭代器
(2)next():调用该方法则可得到下一个返回值
例如:
>>> str = "qwe123"
>>> it = iter(str)
>>> next(it)
'q'
>>> next(it)
'w'
>>> next(it)
'e'
>>> next(it)
'1'
>>> next(it)
'2'
>>> next(it)
'3'
魔法方法:(1)__iter__():若设置容器为迭代器,则必须实现该方法,即返回迭代器本身
(2)__next__():决定迭代的规则
>>> class MyFib:
... def __init__(self, n = 20):
... self.a = 0
... self.b = 1
... self.n = n
... def __iter__(self):
... return self
... def __next__(self):
... self.a, self.b = self.b, self.a + self.b
... return self.a
>>> fibs = MyFib()
>>> for each in fibs:
... print(each)
1
1
2
3
5
8
13
>>> fibs = MyFib(10)
>>> for each in fibs:
... print(each)
1
2
3
4
5
8
2.生成器:生成器可暂时挂起函数,并保留函数的局部变量等数据,再次调用时可从上一次暂停的位置继续执行。
>>> def TestGen():
... print("starting...")
... while 1:
... num = yield 111
... print("num:",num)
>>> tg = TestGen()
>>> next(tg)
starting...
111
>>> print("*" * 20)
********************
>>> next(tg)
num: None
111
>>> def TestG():
... print("starting...")
... yield 111
... yield 222
>>> tg1 = TestG()
>>> next(tg1)
starting...
111
>>> next(tg1)
222
>>> next(tg1)
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
next(tg1)
StopIteration
yield 关键字的生动理解:
(python中yield的用法详解——最简单,最清晰的解释_冯爽朗的博客-CSDN博客_yield)
小练习:斐波那契数列
>>> def fibs():
... a = 0
... b = 1
... while True:
... a, b = b , a + b
... yield a
>>> for each in fibs():
... if each > 20:
... break
... print(each)
1
1
2
3
5
8
13
3. 生成器表达式
(1)列表推导式:
>>> [i * 2 for i in range(10)]
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
(2)字典推导式:
>>> {i : i % 2 == 0 for i in range(10)}
{0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}
(3)集合推导式:
>>> {i for i in [0, 1, 1, 3, 5, 5, 7, 9, 9]}
{0, 1, 3, 5, 7, 9}
(4)生成器表达式:
# 用小括号括起来不是元组推导式,而是生成器表达式
>>> (i for i in range(10))
<generator object <genexpr> at 0x000002BAF50F3740>
>>> a = (i for i in range(10))
>>> next(a)
0
>>> next(a)
1
>>> for each in a:
... print(each)
2
3
4
5
6
7
8
9
八、其他
1. Python 中几种下划线的含义可参考下方链接