《小甲鱼零基础入门学习Python》自学笔记(六)—— 魔法方法

魔法方法

一、Python 魔法方法详解

https://fishc.com.cn/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

二、构造和析构

  • 魔法方法总是被双下划线包围,例如:__init__方法
  • 魔法方法是面向对象的Python的一切,如果你不知道魔法方法,说明你还没能意识到面向对象的Python的强大
  • 魔法方法的"魔力"体现在它们总能够在适当的时候被自动调用

(1)__init__(self[, ...])
在需要对对象进行初始化操作时,才会用到。
必须return None
不能另加return,会报错!

(2)__new__(cls[, ...])
实例化对象时,第一个被调用的方法。
默认执行,一般不需要重写。
需要重写的情况:当继承一个不可变类型,又需要修改的时候,那么它的特性就显得尤为重要。

eg:
>>> class CapStr(str):
	def __new__(cls, string):
		string = string.upper()
		return str.__new__(cls, string)

	
>>> a = CapStr('Love')
>>> a
'LOVE'

这里的CapStr是要继承str,但是CapStr又要把输入的字符串变成大写,str没有这个性质,它是不能改变的,类CapStr就成了继承str且能把输入变大写的类

(3)__del__(self)
析构器。
当对象将要被销毁的时候,这个方法就会被调用。
并非del x就相当于自动调用x.__del__()__del__方法是当垃圾回收机制回收这个对象的时候调用的。
eg:

>>> class C:
	def __init__(self):
		print('我是__init__方法,我被调用了!')
	def __del__(self):
		print('我是__del__方法,我被调用了!')

		
>>> c1 = C()
我是__init__方法,我被调用了!
>>> c2 = c1
>>> c3 = c2
>>> del c3
>>> del c2
>>> del c1
我是__del__方法,我被调用了!

要删除所有的实例化对象时,才会被调用__del__方法。

三、算术运算

算数运算符
https://fishc.com.cn/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

反运算
https://fishc.com.cn/forum.php?mod=viewthread&tid=48793&extra=page%3D1%26filter%3Dtypeid%26typeid%3D403

eg:

>>> class Nint(int):
	def __rsub__(self, other):
		return int.__sub__(self, other)

	
>>> a = Nint(5)
>>> 2 - a
3

当执行反运算时,aself2other。所以2 - a其实是5-2,结果为3。

四、属性访问

  • __getattr__(self, name)

定义当用户试图获取一个不存在的属性时的行为

  • __getattribute__(self, name)

定义当该类的属性被访问时的行为

  • __setattr__(self, name, value)

定义当一个属性被设置时的行为

  • __delattr__(self, name)

定义当一个属性被删除时的行为

eg:
写一个矩形类,默认有宽和高两个属性;如果为一个叫square的属性赋值,那么说明这是一个正方形,值就是正方形的边长,此时宽和高都应该等于边长。

class Rectangle:
    def __init__(self, width=0, height=0):
        self.width = width
        self.height = height

    def __setattr__(self, name, value):
        if name == 'square':
            self.width = value
            self.height = value
        else:
            super().__setattr__(name, value)

    def getArea(self):
        return self.width * self.height

运行:

>>> r = Rectangle(4, 5)
>>> r.getArea()
20
>>> r.square = 7
>>> r.getArea()
49

五、描述符

描述符就是将某种特殊类型的类的实例指派给另一个类的属性。

特殊类型:至少要实现以下三个方法的其中一个。

  • __get__(self, instance, owner)

用于访问属性,返回属性的值

  • __set__(self, instance, value)

将在属性分配操作中调用,不返回任何内容

  • __delete__(self, instance)

控制删除操作,不返回任何内容

eg:

>>> class MyDecriptor:

	def __get__(self, instance, owner):
		print('getting...', self, instance, owner)

	def __set__(self, instance, value):
		print('setting...', self, instance, value)

	def __delete__(self, instance):
		print('deleting...', self, instance)

		
>>> class Test:
	x = MyDecriptor()

	
>>> t = Test()
>>> t.x
getting... <__main__.MyDecriptor object at 0x03CE2760> <__main__.Test object at 0x03FF6040> <class '__main__.Test'>
>>> t.x = '美少女'
setting... <__main__.MyDecriptor object at 0x03CE2760> <__main__.Test object at 0x03FF6040> 美少女
>>> del t.x
deleting... <__main__.MyDecriptor object at 0x03CE2760> <__main__.Test object at 0x03FF6040>

Property的原理:

重写MyProperty类(作用与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(instance)

		
>>> class C:
	def __init__(self):
		self.s = None
		
	def getS(self):
		return self.s
	
	def setS(self, value):
		self.s = value
		
	def delS(self):
		del self.S
		
	x = MyProperty(getS, setS, delS)

运行:

>>> c = C()
>>> c.s = '美少女'
>>> c.s
'美少女'
>>> del c.s
>>> c.s
Traceback (most recent call last):
  File "<pyshell#52>", line 1, in <module>
    c.s
AttributeError: 'C' object has no attribute 's'

结果与Property相同

六、定制容器

协议(Protocols)

与其他编程语言中的接口很相似,它规定你哪些方法必须要定义。然而,在Python中的协议就显得不那么正式。事实上,在Python中,协议更像是一种指南。

容器类型的协议

  • 如果说你希望定制的容器是不可变的话,你只需要定义__len__()__getitem__()方法。

  • 如果你希望定制的容器是可变的话,除了__len__()__getitem__()方法,你还需要定义__setitem__()__delitem__()两个方法。

    容器类型

在这里插入图片描述

eg:
在这里插入图片描述
代码:

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[4]
9
>>> c2[0]
2
>>> c1[4] + c2[3]
17
>>> c1.count
{0: 0, 1: 0, 2: 0, 3: 0, 4: 2}
>>> c2.count
{0: 1, 1: 0, 2: 0, 3: 1, 4: 0}

七、迭代器

iter()
对于一个容器对象调用,得到迭代器
next()
迭代器返回下一个值(如果迭代器没有值可以返回了,那么就会抛出一个StopIteration的异常)

eg:

>>> 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#7>", line 1, in <module>
    next(it)
StopIteration

for循环的原理:

>>> string = 'FishC'
>>> it = iter(string)
>>> while True:
	try:
		each = next(it)
	except StopIteration:
		break
	print(each)

	
F
i
s
h
C

迭代器的魔法方法:

(1)__iter__()
(2)__next__()

eg:斐波那契数列

>>> class Fibs:
	def __init__(self, n=10):
		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
		if self.a > self.n:
			raise StopIteration			# 用raise语句来引发一个异常
		return self.a

	
>>> fibs = Fibs()
>>> for each in fibs:
	print(each)

	
1
1
2
3
5
8

>>> fibs = Fibs(50)
>>> for each in fibs:
	print(each)

	
1
1
2
3
5
8
13
21
34

八、生成器

迭代器的一种实现

生成器:在普通的函数中加入yield语句。

协同程序
可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始。

eg:

>>> def myGen():
	print("生成器被执行!")
	yield 1
	yield 2

	
>>> g = myGen()
>>> next(g)
生成器被执行!
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
  File "<pyshell#60>", line 1, in <module>
    next(g)
StopIteration
>>> for i in myGen():
	print(i)

	
生成器被执行!
1
2

eg:斐波那契数列

>>> 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, end = ' ')

	
1 1 2 3 5 8 13 
  • 列表推导式
>>> a = [i for i in range(20) if not(i % 2) and i % 3]
>>> a
[2, 4, 8, 10, 14, 16]
  • 字典推导式
>>> b = {i : i % 2 == 0 for i in range(10)}
>>> b
{0: True, 1: False, 2: True, 3: False, 4: True, 5: False, 6: True, 7: False, 8: True, 9: False}
  • 集合推导式
>>> c = {i for i in [1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 4, 3, 1]}
>>> c
{1, 2, 3, 4, 5, 6, 7, 8}
  • 生成器推导式
>>> d = (i for i in range(10) if i % 2 == 0)
>>> d
<generator object <genexpr> at 0x03C06290>
>>> next(d)
0
>>> next(d)
2
>>> for each in d:
	print(each)

	
4
6
8

eg:

>>> sum((i for i in range(10) if i % 2 == 0))
20
>>> sum(i for i in range(10) if i % 2 == 0)
20

一对括号、两对括号,都可以!一对括号更加简洁!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值