共计 3696 个字符,预计需要花费 10 分钟才能阅读完成。
title: python 的特地办法与装璜器
author: Sekyoro
date: 2023-05-07 11:31:24
tags:
- python
categories:
archives:
password:
abstract:
message:
这里将介绍 python 的所谓魔法办法以及装璜器
<!–more–>
魔术办法
个别在类中以双下划线突围的办法就是魔术办法,或者叫非凡办法。
简略来说,Python 的魔术办法是为了利用 Python 的规范办法以及不必去记住规范操作的名称,实现更对立的接口。
例如上面的代码
import collections | |
Card = collections.namedtuple('Card', ['rank', 'suit']) | |
class FrenchDeck: | |
ranks = [str(n) for n in range(2, 11)] + list('JQKA') | |
suits = 'spades diamonds clubs hearts'.split() | |
def __init__(self): | |
self._cards = [Card(rank, suit) for suit in self.suits | |
for rank in self.ranks] | |
def __len__(self): | |
return len(self._cards) | |
def __getitem__(self, item): | |
return self._cards[item] | |
if __name__ == '__main__': | |
print(len(deck)) | |
print(deck[0]) |
__len__
这个特地办法使得调用 len(beer_card)理论调用__len__
getitem 办法把 [] 操作交给了 self._cards 列表,所以咱们的 deck 类主动反对切 片(slicing)操作。
输入如下
52 | |
Card(rank='2', suit='spades') |
另外,仅仅实现了 getitem 办法,对象就变成可迭代的了
for i in deck: | |
print(i) |
同时 __contains__
能够实现 in
办法
除了下面通过非凡办法实现迭代,len(), 切片等办法外,还能够实现相似重载运算符的成果。
from math import hypot | |
class Vector: | |
def __init__(self, x=0, y=0): | |
self.x = x | |
self.y = y | |
def __repr__(self): | |
return 'Vector(%r, %r)' % (self.x, self.y) | |
def __abs__(self): | |
return hypot(self.x, self.y) | |
def __bool__(self): | |
return bool(abs(self)) | |
def __add__(self, other): | |
x = self.x + other.x | |
y = self.y + other.y | |
return Vector(x, y) | |
def __mul__(self, scalar): | |
return Vector(self.x * scalar, self.y * scalar) |
v1 = Vector(3,4) | |
v2 = Vector(4,5) | |
v3 = v1+v2 | |
print(v3) |
输入 Vector(7, 9)
还有 __repr__
和__str__
repr 和 str 的区别在于,后者是在 str() 函数被应用,或是在用 print 函数打印一个对象的时候才被调用的,并且它返回的字符串对终端用户更敌对。
如果你只想实现这两个非凡办法中的一个,__repr__ 是更好的抉择,因为如果一个对象没有 str 函数,而 Python 又须要调用它的时候,解释器会用 repr 作为代替。
{!r}
就是应用 format 语法时候的%r
。因而,咱们只须要关注%r
就好。%r
示意的用repr()
解决;相似于的%s
示意用str()
解决一样
其余特地办法
装璜器
装璜器的作用就是为曾经存在的函数或对象增加额定的性能。
装璜器实质上是一个 Python 函数,它能够让其余函数在不须要做任何代码变动的前提下减少额定性能,装璜器的返回值也是一个函数对象。它常常用于有切面需要的场景,比方:插入日志、性能测试、事务处理、缓存、权限校验等场景。装璜器是解决这类问题的绝佳设计,有了装璜器,咱们就能够抽离出大量与函数性能自身无关的雷同代码并持续重用。
def debug(func): | |
def wrapper(): | |
print("[DEBUG]: enter {}()".format(func.__name__)) | |
return func() | |
return wrapper | |
@debug | |
def hello(): | |
print("hello") | |
hello() |
装璜器波及到闭包的概念, 什么是闭包,一句话说就是,在函数中再嵌套一个函数,并且援用内部函数的变量,这就是一个闭包了
上述无参装璜器能够用于输入日志。
如果要在 wrapper 中拜访参数, 如下
def debug(func): | |
def wrapper(*args, **kwargs): | |
print("[DEBUG]: enter {}()".format(func.__name__)) | |
print("params", args, kwargs) | |
return func(*args, **kwargs) | |
return wrapper | |
@debug | |
def test(a,b): | |
print("this is a test function") |
如果要在装璜器中应用参数, 还要在里面突围一层。
def logging(level): | |
def outwrapper(func): | |
def wrapper(*args, **kwargs): | |
print("[{0}]: enter {1}()".format(level, func.__name__)) | |
return func(*args, **kwargs) | |
return wrapper | |
return outwrapper | |
@logging(level="INFO") | |
def hello(a, b, c): | |
print(a, b, c) |
除了应用函数装璜器也能够是应用类装璜器
class logging(object): | |
def __init__(self, func): | |
self.func = func | |
def __call__(self, *args, **kwargs): | |
print("[DEBUG]: enter {}()".format(self.func.__name__)) | |
return self.func(*args, **kwargs) | |
@logging | |
def hello(a, b, c): | |
print(a, b, c) |
装璜器中应用参数
class logging(object): | |
def __init__(self, level): | |
self.level = level | |
def __call__(self, func): | |
def wrapper(*args, **kwargs): | |
print("[{0}]: enter {1}()".format(self.level, func.__name__)) | |
return func(*args, **kwargs) | |
return wrapper | |
@logging(level="TEST") | |
def hello(a, b, c): | |
print(a, b, c) |
属性
当咱们创立一个类,以定义对象时,咱们可能会心愿一些属性对于外界是只读的,或者心愿创立属性只能按特定的形式拜访或批改。这时就能够应用 Python 中的属性(Property)。
在 Python 中,属性是包装在函数中的代码段,它们可能拦挡对对象属性的拜访和批改。Python 内置了 @property 装璜器,能够用来润饰一个办法,使之成为属性调用。同时,Python 还提供了 @property 润饰器的对应办法的 setter 办法,用于设置属性。
简略来说,属性优化了之前应用公共办法来拜访公有属性得办法同时有更精密的粒度。
class myObj: | |
def __init__(self, name): | |
self.__name = name | |
print("init") | |
@property | |
def name(self): | |
return self.__name | |
if __name__ == '__main__': | |
obj = myObj("hello") | |
print(obj.name) |
输入
init | |
hello |
如果应用 obj.name = "world"
更改属性则会报错.
增加
@name.setter | |
def name(self, newname): | |
self.__name = newname |
能够通过 name 属性更改self.__name
此外还有 deleter
@name.deleter | |
def name(self): | |
self.__name = None |
应用 del 删除属性
del obj.name | |
print(obj.name) |
输入就是None
参考资料
- Python 罕用魔术办法 – 知乎 (zhihu.com)
- 《晦涩的 Python》
- Python 根底(十四)—装璜器 wrapper_luoz_python 的博客 -CSDN 博客
- python 装璜器详解 – 知乎 (zhihu.com)
- Python @property 属性详解 – 知乎 (zhihu.com)
本文由 mdnice 多平台公布