python zipdecrypter_Python中的特殊方法

常见特殊方法

python里面最常见和常用的特殊方法莫过于 __init__() 了. 除了它之外还有一些相对常见的,见下表:

备注

欲实现…

具体实现…

Python内部调用…

初始化一个实例对象

x = MyClass()

x.__init__()

字符串的官方呈现方式

repr(x)

x.__repr__()

转换为字符串

str(x)

x.__str__()

转换为字节对象

bytes(x)

x.__bytes__()

格式化字符串

format(x, format_spec)

x.__format__(format_spec)

在实例对象创建之后会立马执行__init__()方法。如果想控制实例的创建过程,那么需要使用__new__()方法。

依照惯例,__repr__()应该返回一个合法的Python表达式字符串。

print(x)时会调用__str__()方法。

Python 3特有.

format_spec需要符合Format Specification Mini-Language语法规则。

迭代器行为特殊方法

备注

欲实现…

具体实现

Python内部调用…

迭代一个序列

iter(seq)

seq.__iter__()

回去迭代器的下一个值

next(seq)

seq.__next__()

让迭代器逆序

reversed(seq)

seq.__reversed__()

无论何时创建一个新的迭代器,__iter__()方法都会被调用。

每获取一次迭代器的值,__next__()会被执行一次。

__reversed__() 方法不常见,它接收一个序列,然后返回一个迭代器,迭代器生成的元素与原序列的顺序是相反的.

计算属性

备注

欲实现…

具体实现…

Python内部调用…

获取计算属性(无条件的)

x.my_property

x.__getattribute__('my_property')

获取计算属性 (fallback)

x.my_property

x.__getattr__('my_property')

设置属性

x.my_property = value

x.__setattr__('my_property', value)

删除属性

del x.my_property

x.__delattr__('my_property')

列出所有属性和方法

dir(x)

x.__dir__()

如果定义了__getattr__()或__getattribute__()方法,则__dir__()方法是有用的。通常,dir(x)只会列出常规的属性和方法。如果__getattr__()方法动态处理颜色属性,dir(x)不会将颜色列为可用属性之一。覆盖__dir__()方法允许你将颜色作为一个可用属性列出,这对那些希望使用你的类而不深入其内部的人很有帮助。

__getattr__()和__getattribute__()方法之间的区别很微妙但很重要。用两个例子来解释:

class Dynamo:

def __getattr__(self, key):

if key == 'color': ①

return 'PapayaWhip'

else:

raise AttributeError ②

>>> dyn = Dynamo()

>>> dyn.color ③

'PapayaWhip'

>>> dyn.color = 'LemonChiffon'

>>> dyn.color ④

'LemonChiffon'

属性名作为字符串传递到__getattr__()方法中。如果名称是color,该方法返回一个值。(在本例中,它只是一个硬编码的字符串,但通常会执行某种计算并返回结果。)

如果属性名未定义,__getattr__()方法需要引发AttributeError异常,否则代码将在访问未定义的属性时静默失败。(从技术上讲,如果该方法没有引发异常或显式返回值,它将返回None,即Python的空值。这意味着没有显式定义的all属性将是None。)

dyn实例没有一个名为color的属性,因此调用__getattr__()方法来提供一个计算值。

显式设置dyn.color后,__getattr__()方法将不再被调用来为dyn.color提供一个值,因为dyn.color已经在实例中定义了。

另一方面,__getattribute__()方法是绝对和无条件的,也就是属性值写死了,无法更改。

class SuperDynamo:

def __getattribute__(self, key):

if key == 'color':

return 'PapayaWhip'

else:

raise AttributeError

>>> dyn = SuperDynamo()

>>> dyn.color ①

'PapayaWhip'

>>> dyn.color = 'LemonChiffon'

>>> dyn.color ②

'PapayaWhip'

调用__getattribute__()方法来为dyn.color提供一个值。

即使显式地设置了dyn.color, __getattribute__()方法仍然会被调用来为dyn.color提供一个值。如果存在,__getattribute__()方法会被无条件地调用,用于每个属性和方法查找,即使是在创建实例后显式设置的属性。

☞如果你的类定义了一个__getattribute__()方法,你可能还想定义一个__setattr__()方法,并在它们之间协调以跟踪属性值。否则,在创建实例之后,设置的任何属性都将消失在虚空中。

需要格外小心__getattribute__()方法,因为当Python在你的类上查找方法名时,它也会被调用。

class Rastan:

def __getattribute__(self, key):

raise AttributeError ①

def swim(self):

pass

>>> hero = Rastan()

>>> hero.swim() ②

Traceback (most recent call last):

File "", line 1, in

File "", line 3, in __getattribute__

AttributeError

这个类定义了一个__getattribute__()方法,该方法总是引发AttributeError异常。任何属性或方法查找都不会成功。

当你调用hero.swim()时,Python会在Rastan类中查找swim()方法。此查找通过__getattribute__()方法,因为所有属性和方法查找都通过__getattribute__()方法。在本例中,__getattribute__()方法引发AttributeError异常,因此方法查找失败,因此方法调用失败。

函数行为特殊方法

通过定义__call__()方法,可以使一个类的实例成为可调用的——就像一个函数是可调用的一样。

备注

欲实现…

具体实现…

Python内部调用…

像调用函数一样调用实例化对象

my_instance()

my_instance.__call__()

zipfile模块使用这个来定义一个类,该类可以用给定的密码解密加密的zip文件。zip解密算法要求在解密过程中存储状态。将解密器定义为类允许在解密器类的单个实例中维护这种状态。

状态在__init__()方法中初始化,并在文件解密时更新。但由于类也是可调用的像一个函数,你可以传递实例作为map()函数的第一个参数,像这样:

# excerpt from zipfile.py

class _ZipDecrypter:

.

.

.

def __init__(self, pwd):

self.key0 = 305419896 ①

self.key1 = 591751049

self.key2 = 878082192

for p in pwd:

self._UpdateKeys(p)

def __call__(self, c): ②

assert isinstance(c, int)

k = self.key2 | 2

c = c ^ (((k * (k^1)) >> 8) & 255)

self._UpdateKeys(c)

return c

.

.

.

zd = _ZipDecrypter(pwd) ③

bytes = zef_file.read(12)

h = list(map(zd, bytes[0:12])) ④

_ZipDecryptor类以三个旋转密钥的形式维护状态,稍后在_UpdateKeys()方法中更新这些密钥(这里没有显示)。

这个类定义了一个__call__()方法,使类实例像函数一样可调用。在本例中,__call__()方法解密zip文件的单个字节,然后根据被解密的字节更新旋转密钥。

zd是_ZipDecryptor类的一个实例。pwd变量被传递给__init__()方法,在那里它被存储并用于第一次更新旋转键。

给定zip文件的前12个字节,通过将字节映射到zd来解密它们,实际上调用zd 12次,这将调用__call__() 12次,这将更新其内部状态并返回结果字节12次。

集合行为特殊方法

如果类充当一组值的容器——也就是说,查询类是否“包含”一个值——那么它可能应该定义以下特殊方法。

备注

欲实现…

具体实现…

Python内部调用…

获取元素数量

len(s)

s.__len__()

是否包含特殊指定值

x in s

s.__contains__(x)

cgi模块在其FieldStorage类中使用这些方法,该类表示提交到动态web页面的所有表单字段或查询参数。

# A script which responds to http://example.com/search?q=cgi

import cgi

fs = cgi.FieldStorage()

if 'q' in fs: ①

do_search()

# An excerpt from cgi.py that explains how that works

class FieldStorage:

.

.

.

def __contains__(self, key): ②

if self.list is None:

raise TypeError('not indexable')

return any(item.name == key for item in self.list) ③

def __len__(self): ④

return len(self.keys()) ⑤

一旦你创建了一个cgi.FieldStorage类,就可以使用in操作符来检查查询字符串中是否包含特定参数。

__contains__()方法是实现此功能的魔法。当你说if 'q' in fs时,Python会在fs对象上查找__contains__()方法,该方法在cgi.py中定义。值'q'作为key参数传递给__contains__()方法。

any()函数接受一个generator表达式,如果生成器输出任何项,则返回True。any()函数足够智能,只要找到第一个匹配就会停止。

同样的FieldStorage类也支持返回它的长度,所以可以使用len(fs),它会调用FieldStorage类上的__len__()方法来返回它标识的查询参数的数量。

self.keys()方法检self.list查是否为None,因此__len__方法不需要重复这个错误检查。

字典行为特殊方法

备注

欲实现…

具体实现…

Python内部调用…

通过键获取值

x[key]

x.__getitem__(key)

通过键设置值

x[key] = value

x.__setitem__(key, value)

删除键-值对

del x[key]

x.__delitem__(key)

为缺失键设置默认值

x[nonexistent_key]

x.__missing__(nonexistent_key)

cgi模块中的' FieldStorage '类也定义了这些特殊的方法,这意味着你可以这样做:

# A script which responds to http://example.com/search?q=cgi

import cgi

fs = cgi.FieldStorage()

if 'q' in fs:

do_search(fs['q']) ①

# An excerpt from cgi.py that shows how it works

class FieldStorage:

.

.

.

def __getitem__(self, key): ②

if self.list is None:

raise TypeError('not indexable')

found = []

for item in self.list:

if item.name == key: found.append(item)

if not found:

raise KeyError(key)

if len(found) == 1:

return found[0]

else:

return found

fs对象是cgi.FieldStorage的一个实例。但是仍然可以计算像fs['q']这样的表达式。

fs['q']调用__getitem__()方法,键参数设置为'q'。然后在其内部维护的查询参数列表(self.list)中查找.name与给定键匹配的项。

数行为特殊方法

使用适当的特殊方法,可以定义自己的类,使它们的行为类似于数字。也就是说,可以对它们进行加、减和其他数学运算。这就是分数是如何实现的- Fraction类实现了这些特殊的方法,然后你可以这样做:

from fractions import Fraction

>>> x = Fraction(1, 3) # 分子为1, 分母为3

>>> x / 3

Fraction(1, 9)

以下是实现数字类所需的特殊方法的完整列表。

备注

欲实现…

具体实现…

Python内部调用…

x + y

x.__add__(y)

x - y

x.__sub__(y)

x * y

x.__mul__(y)

x / y

x.__truediv__(y)

向下取整

x // y

x.__floordiv__(y)

求模

x % y

x.__mod__(y)

向下取整 & 求模

divmod(x, y)

x.__divmod__(y)

乘方

x ** y

x.__pow__(y)

左位移

x << y

x.__lshift__(y)

右位移

x >> y

x.__rshift__(y)

位与and

x & y

x.__and__(y)

位异或 xor

x ^ y

x.__xor__(y)

位或or

x | y

x.__or__(y)

如果x是实现这些方法的类的实例,那就很好了。但如果它没有实现其中的一个呢?或者更糟,如果它实现了它,但不能处理某些类型的参数怎么办?例如:

from fractions import Fraction

>>> x = Fraction(1, 3)

>>> 1 / x

Fraction(3, 1)

这并非取一个分数除以一个整数的情况(如前面的例子)。这种情况很简单:x / 3调用x.__truediv__(3),而Fraction类的__truediv__()方法处理所有的数学运算。但是整数不知道如何用分数做算术运算。那么为什么这个例子行得通呢?

第二组算术特殊方法具有反射操作数。给定一个需要两个操作数的算术运算(例如:x / y),有两种方法:

让x除以y, 或者

让y分解成x

备注

欲实现…

具体实现…

Python内部调用…

x + y

y.__radd__(x)

x - y

y.__rsub__(x)

x * y

y.__rmul__(x)

x / y

y.__rtruediv__(x)

向下取整

x // y

y.__rfloordiv__(x)

取模

x % y

y.__rmod__(x)

向下取整 & 取模

divmod(x, y)

y.__rdivmod__(x)

乘方

x ** y

y.__rpow__(x)

左位移

x << y

y.__rlshift__(x)

右位移

x >> y

y.__rrshift__(x)

按位 and

x & y

y.__rand__(x)

按位xor

x ^ y

y.__rxor__(x)

按位or

x | y

y.__ror__(x)

但是等等! 还有更多!如果正在执行原位操作,比如x /= 3,那么甚至可以定义更多特殊的方法。

备注

欲实现…

具体实现…

Python内部调用…

原位 加

x += y

x.__iadd__(y)

原位减

x -= y

x.__isub__(y)

原位 乘

x *= y

x.__imul__(y)

原位 除

x /= y

x.__itruediv__(y)

原位向下取整

x //= y

x.__ifloordiv__(y)

原位取模

x %= y

x.__imod__(y)

原位乘方

x **= y

x.__ipow__(y)

原位位左移

x <<= y

x.__ilshift__(y)

原位位右移

x >>= y

x.__irshift__(y)

原位按位 and

x &= y

x.__iand__(y)

原位按位 xor

x ^= y

x.__ixor__(y)

原位按位 or

x |= y

x.__ior__(y)

如果想对原位操作数做一些特殊的优化,只需要定义原位方法,比如__itruediv__()方法。否则,Python将从本质上重新定义原位操作数,使用常规操作数+变量赋值。

还有一些一元数学运算,可以作用在类数对象自己。

备注

欲实现…

具体实现…

Python内部调用…

取负

-x

x.__neg__()

取正

+x

x.__pos__()

绝对值

abs(x)

x.__abs__()

逆/反

~x

x.__invert__()

复数

complex(x)

x.__complex__()

整数

int(x)

x.__int__()

浮点数

float(x)

x.__float__()

四舍五入

round(x)

x.__round__()

四舍五入并设置小数点

round(x, n)

x.__round__(n)

最小取整 >= x

math.ceil(x)

x.__ceil__()

最大取整 <= x

math.floor(x)

x.__floor__()

将x截断为接近0的整数

math.trunc(x)

x.__trunc__()

PEP 357

数字作为列表索引

a_list[x]

a_list[x.__index__()]

可比较行为特殊方法

备注

欲实现…

具体实现…

Python内部调用…

相等

x == y

x.__eq__(y)

不相等

x != y

x.__ne__(y)

小于

x < y

x.__lt__(y)

不大于

x <= y

x.__le__(y)

大于

x > y

x.__gt__(y)

不小于

x >= y

x.__ge__(y)

布尔上下文中的真值

if x:

x.__bool__()

☞如果只定义__lt__()方法但没有__gt__()方法,Python将使用__lt__()方法,并交换操作数。然而,Python不会组合方法。例如,如果你定义了一个__lt__()方法和一个__eq__()方法并尝试测试是否x <= y, Python并不会依次调用__lt__()和__eq__()。它只会调用__le__()方法。😺

序列化行为特殊方法

Python支持序列化和反序列化任意对象。(大多数Python教程将此过程称为pickling和unpickling。)这对于将状态保存到文件并在稍后恢复文件非常有用。所有的原生数据类型都已经支持pickle。如果创建了一个希望能够pickle的自定义类,那么请阅读pickle协议,了解何时以及如何调用以下特殊方法。

备注

欲实现…

具体实现…

Python内部调用…

自定义对象复制

copy.copy(x)

x.__copy__()

深度复制

copy.deepcopy(x)

x.__deepcopy__()

*

pickling之前获取对象状态

pickle.dump(x, file)

x.__getstate__()

*

序列化对象

pickle.dump(x, file)

x.__reduce__()

*

序列化对象

pickle.dump(x, file, protocol_version)

x.__reduce_ex__(protocol_version)

*

加载序列化对象

x = pickle.load(file)

x.__getnewargs__()

*

unpickling之后恢复对象的状态

x = pickle.load(file)

x.__setstate__()

* 要重新创建一个序列化对象,Python需要创建一个看起来像序列化对象的新对象,然后在新对象上设置所有属性的值。__getnewargs__()方法控制对象的创建方式,然后__setstate__()方法控制属性值的恢复方式。

with 语句块行为特殊方法

一个with块定义了一个运行时上下文;当执行with语句时,进入上下文,在执行块中的最后一条语句后,退出上下文。

备注

欲实现…

具体实现…

Python内部调用…

进入with 块语句时执行一些代码

with x:

x.__enter__()

离开 with 块语句是执行一些代码

with x:

x.__exit__(exc_type, exc_value, traceback)

举例:

# excerpt from io.py:

def _checkClosed(self, msg=None):

'''Internal: raise an ValueError if file is closed

'''

if self.closed:

raise ValueError('I/O operation on closed file.'

if msg is None else msg)

def __enter__(self):

'''Context management protocol. Returns self.'''

self._checkClosed() ①

return self ②

def __exit__(self, *args):

'''Context management protocol. Calls close()'''

self.close() ③

file对象同时定义了__enter__()和__exit__()方法。__enter__()方法检查文件是否已打开;如果不是,_checkClosed()方法将引发异常。

__enter__()方法应该几乎总是返回self -这是with块将用来分派属性和方法的对象。

在with块之后,file对象自动关闭。怎么做到的?在__exit__()方法中,它调用self.close()。

☞__exit__()方法将始终被调用,即使在with块内引发异常。事实上,如果异常被引发,异常信息将被传递给'__exit__()方法。

一些深奥的东西

如果你够牛,那么就可以完全控制类的实现,比如定义属性,决定哪些类属于你的子类等等。

备注

欲实现…

具体实现…

Python内部调用…

类构造器

x = MyClass()

x.__new__()

*

销毁类

del x

x.__del__()

定义一组特定属性

x.__slots__()

自定义hash 值

hash(x)

x.__hash__()

获取属性值

x.color

type(x).__dict__['color'].__get__(x, type(x))

设置属性值

x.color = 'PapayaWhip'

type(x).__dict__['color'].__set__(x, 'PapayaWhip')

删除属性

del x.color

type(x).__dict__['color'].__del__(x)

控制一个对象是否为你的类的实例

isinstance(x, MyClass)

MyClass.__instancecheck__(x)

控制一个类是否是你的类的子类

issubclass(C, MyClass)

MyClass.__subclasscheck__(C)

控制一个类是否为抽象基类的子类

issubclass(C, MyABC)

MyABC.__subclasshook__(C)

* Python调用 __del__() 特殊方法是很复杂的,设计到Python的内存回收机制等,我也不是很懂,就不多说了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值