当前位置: 首页 > news >正文

本人已履行网站备案信息如何开公众号微信公众平台

本人已履行网站备案信息,如何开公众号微信公众平台,滨州做网站建设价格,长沙外贸建站哪里好Python 类中#xff0c;凡是以双下划线 __ 开头和结尾命名的成员#xff08;属性和方法#xff09;#xff0c;都被称为类的特殊成员#xff08;特殊属性和特殊方法#xff09;。例如#xff0c;类的 __init__(self) 构造方法就是典型的特殊方法。 Python …Python 类中凡是以双下划线 __ 开头和结尾命名的成员属性和方法都被称为类的特殊成员特殊属性和特殊方法。例如类的 __init__(self) 构造方法就是典型的特殊方法。 Python 类中的特殊成员其特殊性类似 C 类的 private 私有成员即不能在类的外部直接调用但允许借助类中的普通方法调用甚至修改它们。如果需要还可以对类的特殊方法进行重写从而实现一些特殊的功能。 当然除了 __init__(self) 之外Python 类中还含有很多特殊成员包括 __del__(self)、__new__(self) 等本章会一一为你进行详细的讲解。 一、Python __new__()方法详解 __new__() 是一种负责创建类实例的静态方法它无需使用 staticmethod 装饰器修饰且该方法会优先 __init__() 初始化方法被调用。 一般情况下覆写 __new__() 的实现将会使用合适的参数调用其超类的 super().__new__()并在返回之前修改实例。例如 class demoClass:instances_created 0def __new__(cls,*args,**kwargs):print(__new__():,cls,args,kwargs)instance super().__new__(cls)instance.number cls.instances_createdcls.instances_created 1return instancedef __init__(self,attribute):print(__init__():,self,attribute)self.attribute attribute test1 demoClass(abc) test2 demoClass(xyz) print(test1.number,test1.instances_created) print(test2.number,test2.instances_created) 输出结果为 __new__(): class __main__.demoClass (abc,) {} __init__(): __main__.demoClass object at 0x0000026FC0DF8080 abc __new__(): class __main__.demoClass (xyz,) {} __init__(): __main__.demoClass object at 0x0000026FC0DED358 xyz 0 2 1 2 __new__() 通常会返回该类的一个实例但有时也可能会返回其他类的实例如果发生了这种情况则会跳过对 __init__() 方法的调用。而在某些情况下比如需要修改不可变类实例Python 的某些内置类型的创建行为利用这一点会事半功倍。比如 class nonZero(int):def __new__(cls,value):return super().__new__(cls,value) if value ! 0 else Nonedef __init__(self,skipped_value):#此例中会跳过此方法print(__init__())super().__init__() print(type(nonZero(-12))) print(type(nonZero(0))) 运行结果为 __init__() class __main__.nonZero class NoneType 那么什么情况下使用 __new__() 呢答案很简单在 __init__() 不够用的时候。 例如前面例子中对 Python 不可变的内置类型如 int、str、float 等进行了子类化这是因为一旦创建了这样不可变的对象实例就无法在 __init__() 方法中对其进行修改。 有些读者可能会认为__new__() 对执行重要的对象初始化很有用如果用户忘记使用 super()可能会漏掉这一初始化。虽然这听上去很合理但有一个主要的缺点即如果使用这样的方法那么即便初始化过程已经是预期的行为程序员明确跳过初始化步骤也会变得更加困难。不仅如此它还破坏了“__init__() 中执行所有初始化工作”的潜规则。 注意由于 __new__() 不限于返回同一个类的实例所以很容易被滥用不负责任地使用这种方法可能会对代码有害所以要谨慎使用。一般来说对于特定问题最好搜索其他可用的解决方案最好不要影响对象的创建过程使其违背程序员的预期。比如说前面提到的覆写不可变类型初始化的例子完全可以用工厂方法一种设计模式来替代。 Python中大量使用 __new__() 方法且合理的就是 MetaClass 元类。有关元类的介绍可阅读《Python MetaClass元类》一节。 二、Python __repr__()方法显示属性 前面介绍中我们经常会直接输出类的实例化对象例如 class CLanguage:pass clangs CLanguage() print(clangs) 程序运行结果为 __main__.CLanguage object at 0x000001A7275221D0 通常情况下直接输出某个实例化对象本意往往是想了解该对象的基本信息例如该对象有哪些属性它们的值各是多少等等。但默认情况下我们得到的信息只会是“类名object at内存地址”对我们了解该实例化对象帮助不大。 那么有没有可能自定义输出实例化对象时的信息呢答案是肯定通过重写类的 __repr__() 方法即可。事实上当我们输出某个实例化对象时其调用的就是该对象的 __repr__() 方法输出的是该方法的返回值。 以本节开头的程序为例执行 print(clangs) 等同于执行 print(clangs.__repr__())程序的输出结果是一样的输出的内存地址可能不同。 和 __init__(self) 的性质一样Python 中的每个类都包含 __repr__() 方法因为 object 类包含 __reper__() 方法而 Python 中所有的类都直接或间接继承自 object 类。   默认情况下__repr__() 会返回和调用者有关的 “类名object at内存地址”信息。当然我们还可以通过在类中重写这个方法从而实现当输出实例化对象时输出我们想要的信息。 举个例子 class CLanguage:def __init__(self):self.name C语言中文网self.add http://c.biancheng.netdef __repr__(self):return CLanguage[name self.name ,add self.add ] clangs CLanguage() print(clangs) 程序运行结果为 CLanguage[nameC语言中文网,addhttp://c.biancheng.net] 由此可见__repr__() 方法是类的实例化对象用来做“自我介绍”的方法默认情况下它会返回当前对象的“类名object at内存地址”而如果对该方法进行重写可以为其制作自定义的自我描述信息。 三、Python __del__()方法销毁对象 我们知道Python 通过调用 __init__() 方法构造当前类的实例化对象而本节要学的 __del__() 方法功能正好和 __init__() 相反其用来销毁实例化对象。 事实上在编写程序时如果之前创建的类实例化对象后续不再使用最好在适当位置手动将其销毁释放其占用的内存空间整个过程称为垃圾回收简称GC。 大多数情况下Python 开发者不需要手动进行垃圾回收因为 Python 有自动的垃圾回收机制下面会讲能自动将不需要使用的实例对象进行销毁。 无论是手动销毁还是 Python 自动帮我们销毁都会调用 __del__() 方法。举个例子 class CLanguage:def __init__(self):print(调用 __init__() 方法构造对象)def __del__(self):print(调用__del__() 销毁对象释放其空间) clangs CLanguage() del clangs 程序运行结果为 调用 __init__() 方法构造对象 调用__del__() 销毁对象释放其空间 但是读者千万不要误认为只要为该实例对象调用 __del__() 方法该对象所占用的内存空间就会被释放。举个例子 class CLanguage:def __init__(self):print(调用 __init__() 方法构造对象)def __del__(self):print(调用__del__() 销毁对象释放其空间) clangs CLanguage() #添加一个引用clangs对象的实例对象 cl clangs del clangs print(***********) 程序运行结果为 调用 __init__() 方法构造对象 *********** 调用__del__() 销毁对象释放其空间 注意最后一行输出信息是程序执行即将结束时调用 __del__() 方法输出的。 可以看到当程序中有其它变量比如这里的 cl引用该实例对象时即便手动调用 __del__() 方法该方法也不会立即执行。这和 Python 的垃圾回收机制的实现有关。 Python 采用自动引用计数简称 ARC的方式实现垃圾回收机制。该方法的核心思想是每个 Python 对象都会配置一个计数器初始 Python 实例对象的计数器值都为 0如果有变量引用该实例对象其计数器的值会加 1依次类推反之每当一个变量取消对该实例对象的引用计数器会减 1。如果一个 Python 对象的的计数器值为 0则表明没有变量引用该 Python 对象即证明程序不再需要它此时 Python 就会自动调用 __del__() 方法将其回收。 以上面程序中的 clangs 为例实际上构建 clangs 实例对象的过程分为 2 步先使用 CLanguage() 调用该类中的 __init__() 方法构造出一个该类的对象将其称为 C计数器为 0并立即用 clangs 这个变量作为所建实例对象的引用 C 的计数器值 1。在此基础上又有一个 clang 变量引用 clangs其实相当于引用 CLanguage()此时 C 的计数器再 1 这时如果调用del clangs语句只会导致 C 的计数器减 1值变为 1因为 C 的计数器值不为 0因此 C 不会被销毁不会执行 __del__() 方法。 如果在上面程序结尾添加如下语句 del cl print(-----------) 则程序的执行结果为调用 __init__() 方法构造对象 *********** 调用__del__() 销毁对象释放其空间 ----------- 可以看到当执行 del cl 语句时其应用的对象实例对象 C 的计数器继续 -1变为 0对于计数器为 0 的实例对象Python 会自动将其视为垃圾进行回收。 需要额外说明的是如果我们重写子类的 __del__() 方法父类为非 object 的类则必须显式调用父类的 __del__() 方法这样才能保证在回收子类对象时其占用的资源可能包含继承自父类的部分资源能被彻底释放。为了说明这一点这里举一个反例 class CLanguage:def __del__(self):print(调用父类 __del__() 方法)class cl(CLanguage):def __del__(self):print(调用子类 __del__() 方法) c cl() del c 程序运行结果为 调用子类 __del__() 方法 四、Python __dir__()用法列出对象的所有属性方法名 前面在介绍 Python 内置函数时提到了 dir() 函数通过此函数可以某个对象拥有的所有的属性名和方法名该函数会返回一个包含有所有属性名和方法名的有序列表。 举个例子 class CLanguage:def __init__ (self,):self.name C语言中文网self.add http://c.biancheng.netdef say():pass clangs CLanguage() print(dir(clangs)) 程序运行结果为 [__class__, __delattr__, __dict__, __dir__, __doc__, __eq__, __format__, __ge__, __getattribute__, __gt__, __hash__, __init__, __init_subclass__, __le__, __lt__, __module__, __ne__, __new__, __reduce__, __reduce_ex__, __repr__, __setattr__, __sizeof__, __str__, __subclasshook__, __weakref__, add, name, say] 注意通过 dir() 函数不仅仅输出本类中新添加的属性名和方法最后 3 个还会输出从父类这里为 object 类继承得到的属性名和方法名。 值得一提的是dir() 函数的内部实现其实是在调用参数对象 __dir__() 方法的基础上对该方法返回的属性名和方法名做了排序。 所以除了使用 dir() 函数我们完全可以自行调用该对象具有的 __dir__() 方法 class CLanguage:def __init__ (self,):self.name C语言中文网self.add http://c.biancheng.netdef say():pass clangs CLanguage() print(clangs.__dir__()) 程序运行结果为 [name, add, __module__, __init__, say, __dict__, __weakref__, __doc__, __repr__, __hash__, __str__, __getattribute__, __setattr__, __delattr__, __lt__, __le__, __eq__, __ne__, __gt__, __ge__, __new__, __reduce_ex__, __reduce__, __subclasshook__, __init_subclass__, __format__, __sizeof__, __dir__, __class__] 显然使用 __dir__() 方法和 dir() 函数输出的数据是相同仅仅顺序不同。 五、Python __dict__属性查看对象内部所有属性名和属性值组成的字典 在 Python 类的内部无论是类属性还是实例属性都是以字典的形式进行存储的其中属性名作为键而值作为该键对应的值。 为了方便用户查看类中包含哪些属性Python 类提供了 __dict__ 属性。需要注意的一点是该属性可以用类名或者类的实例对象来调用用类名直接调用 __dict__会输出该由类中所有类属性组成的字典而使用类的实例对象调用 __dict__会输出由类中所有实例属性组成的字典。 举个例子 class CLanguage:a 1b 2def __init__ (self):self.name C语言中文网self.add http://c.biancheng.net #通过类名调用__dict__ print(CLanguage.__dict__)#通过类实例对象调用 __dict__ clangs CLanguage() print(clangs.__dict__) 程序输出结果为 {__module__: __main__, a: 1, b: 2, __init__: function CLanguage.__init__ at 0x0000022C69833E18, __dict__: attribute __dict__ of CLanguage objects, __weakref__: attribute __weakref__ of CLanguage objects, __doc__: None} {name: C语言中文网, add: http://c.biancheng.net} 不仅如此对于具有继承关系的父类和子类来说父类有自己的 __dict__同样子类也有自己的 __dict__它不会包含父类的 __dict__。例如 class CLanguage:a 1b 2def __init__ (self):self.name C语言中文网self.add http://c.biancheng.netclass CL(CLanguage):c 1d 2def __init__ (self):self.na Python教程self.ad http://c.biancheng.net/python #父类名调用__dict__ print(CLanguage.__dict__) #子类名调用__dict__ print(CL.__dict__)#父类实例对象调用 __dict__ clangs CLanguage() print(clangs.__dict__) #子类实例对象调用 __dict__ cl CL() print(cl.__dict__) 运行结果为 {__module__: __main__, a: 1, b: 2, __init__: function CLanguage.__init__ at 0x000001721A853E18, __dict__: attribute __dict__ of CLanguage objects, __weakref__: attribute __weakref__ of CLanguage objects, __doc__: None} {__module__: __main__, c: 1, d: 2, __init__: function CL.__init__ at 0x000001721CD15510, __doc__: None} {name: C语言中文网, add: http://c.biancheng.net} {na: Python教程, ad: http://c.biancheng.net/python} 显然通过子类直接调用的 __dict__ 中并没有包含父类中的 a 和 b 类属性同样通过子类对象调用的 __dict__也没有包含父类对象拥有的 name 和 add 实例属性。 除此之外借助由类实例对象调用 __dict__ 属性获取的字典可以使用字典的方式对其中实例属性的值进行修改例如 class CLanguage:a aaab 2def __init__ (self):self.name C语言中文网self.add http://c.biancheng.net#通过类实例对象调用 __dict__ clangs CLanguage() print(clangs.__dict__) clangs.__dict__[name] Python教程 print(clangs.name) 程序运行结果为 {name: C语言中文网, add: http://c.biancheng.net} Python教程 注意无法通过类似的方式修改类变量的值。 六、Python setattr()、getattr()、hasattr()函数用法详解 除了前面介绍的几个类中的特殊方法外本节再介绍 3 个常用的函数分别是 hasattr()、getattr() 以及 setattr。 1、Python hasattr()函数 hasattr() 函数用来判断某个类实例对象是否包含指定名称的属性或方法。该函数的语法格式如下 hasattr(obj, name) 其中 obj 指的是某个类的实例对象name 表示指定的属性名或方法名。同时该函数会将判断的结果True 或者 False作为返回值反馈回来。 举个例子 class CLanguage:def __init__ (self):self.name C语言中文网self.add http://c.biancheng.netdef say(self):print(我正在学Python)clangs CLanguage() print(hasattr(clangs,name)) print(hasattr(clangs,add)) print(hasattr(clangs,say)) 程序输出结果为 True True True 显然无论是属性名还是方法名都在 hasattr() 函数的匹配范围内。因此我们只能通过该函数判断实例对象是否包含该名称的属性或方法但不能精确判断该名称代表的是属性还是方法。 2、Python getattr() 函数 getattr() 函数获取某个类实例对象中指定属性的值。没错和 hasattr() 函数不同该函数只会从类对象包含的所有属性中进行查找。 getattr() 函数的语法格式如下 getattr(obj, name[, default]) 其中obj 表示指定的类实例对象name 表示指定的属性名而 default 是可选参数用于设定该函数的默认返回值即当函数查找失败时如果不指定 default 参数则程序将直接报 AttributeError 错误反之该函数将返回 default 指定的值。 举个例子 class CLanguage:def __init__ (self):self.name C语言中文网self.add http://c.biancheng.netdef say(self):print(我正在学Python)clangs CLanguage() print(getattr(clangs,name)) print(getattr(clangs,add)) print(getattr(clangs,say)) print(getattr(clangs,display,nodisplay)) 程序执行结果为 C语言中文网 http://c.biancheng.net bound method CLanguage.say of __main__.CLanguage object at 0x000001FC2F2E3198 nodisplay 可以看到对于类中已有的属性getattr() 会返回它们的值而如果该名称为方法名则返回该方法的状态信息反之如果该明白不为类对象所有要么返回默认的参数要么程序报 AttributeError 错误。 3、Python setattr()函数 setattr() 函数的功能相对比较复杂它最基础的功能是修改类实例对象中的属性值。其次它还可以实现为实例对象动态添加属性或者方法。 setattr() 函数的语法格式如下 setattr(obj, name, value) 首先下面例子演示如何通过该函数修改某个类实例对象的属性值 class CLanguage:def __init__ (self):self.name C语言中文网self.add http://c.biancheng.netdef say(self):print(我正在学Python) clangs CLanguage() print(clangs.name) print(clangs.add) setattr(clangs,name,Python教程) setattr(clangs,add,http://c.biancheng.net/python) print(clangs.name) print(clangs.add) 程序运行结果为 C语言中文网 http://c.biancheng.net Python教程 http://c.biancheng.net/python 甚至利用 setattr() 函数还可以将类属性修改为一个类方法同样也可以将类方法修改成一个类属性。例如 def say(self):print(我正在学Python)class CLanguage:def __init__ (self):self.name C语言中文网self.add http://c.biancheng.netclangs CLanguage() print(clangs.name) print(clangs.add) setattr(clangs,name,say) clangs.name(clangs) 程序运行结果为 C语言中文网 http://c.biancheng.net 我正在学Python 显然通过修改 name 属性的值为 say这是一个外部定义的函数原来的 name 属性就变成了一个 name() 方法。 使用 setattr() 函数对实例对象中执行名称的属性或方法进行修改时如果该名称查找失败Python 解释器不会报错而是会给该实例对象动态添加一个指定名称的属性或方法。例如 def say(self):print(我正在学Python)class CLanguage:passclangs CLanguage() setattr(clangs,name,C语言中文网) setattr(clangs,say,say) print(clangs.name) clangs.say(clangs) 程序执行结果为 C语言中文网 我正在学Python 可以看到虽然 CLanguage 为空类但通过 setattr() 函数我们为 clangs 对象动态添加了一个 name 属性和一个 say() 方法。 七、Python issubclass和isinstance函数检查类型 Python 提供了如下两个函数来检查类型 issubclass(cls, class_or_tuple)检查 cls 是否为后一个类或元组包含的多个类中任意类的子类。isinstance(obj, class_or_tuple)检查 obj 是否为后一个类或元组包含的多个类中任意类的对象。 通过使用上面两个函数程序可以方便地先执行检查然后才调用方法这样可以保证程序不会出现意外情况。 如下程序示范了通过这两个函数来检查类型 # 定义一个字符串 hello Hello; # Hello是str类的实例输出True print(Hello是否是str类的实例: , isinstance(hello, str)) # Hello是object类的子类的实例输出True print(Hello是否是object类的实例: , isinstance(hello, object)) # str是object类的子类输出True print(str是否是object类的子类: , issubclass(str, object)) # Hello不是tuple类及其子类的实例输出False print(Hello是否是tuple类的实例: , isinstance(hello, tuple)) # str不是tuple类的子类输出False print(str是否是tuple类的子类: , issubclass(str, tuple)) # 定义一个列表 my_list [2, 4] # [2, 4]是list类的实例输出True print([2, 4]是否是list类的实例: , isinstance(my_list, list)) # [2, 4]是object类的子类的实例输出True print([2, 4]是否是object类及其子类的实例: , isinstance(my_list, object)) # list是object类的子类输出True print(list是否是object类的子类: , issubclass(list, object)) # [2, 4]不是tuple类及其子类的实例输出False print([2, 4]是否是tuple类及其子类的实例: , isinstance([2, 4], tuple)) # list不是tuple类的子类输出False print(list是否是tuple类的子类: , issubclass(list, tuple)) 通过上面程序可以看出issubclass() 和 isinstance() 两个函数的用法差不多区别只是 issubclass() 的第一个参数是类名而 isinstance() 的第一个参数是变量这也与两个函数的意义对应issubclass 用于判断是否为子类而 isinstance() 用于判断是否为该类或子类的实例。 issubclass() 和 isinstance() 两个函数的第二个参数都可使用元组。例如如下代码 data (20, fkit) print(data是否为列表或元组: , isinstance(data, (list, tuple))) # True # str不是list或者tuple的子类输出False print(str是否为list或tuple的子类: , issubclass(str, (list, tuple))) # str是list或tuple或object的子类输出True print(str是否为list或tuple或object的子类 , issubclass(str, (list, tuple, object))) 此外Python 为所有类都提供了一个 __bases__ 属性通过该属性可以查看该类的所有直接父类该属性返回所有直接父类组成的元组。例如如下代码 class A:pass class B:pass class C(A, B):pass print(类A的所有父类:, A.__bases__) print(类B的所有父类:, B.__bases__) print(类C的所有父类:, C.__bases__) 运行上面程序可以看到如下运行结果 类A的所有父类: (class object,) 类B的所有父类: (class object,) 类C的所有父类: (class __main__.A, class __main__.B) 从上面的运行结果可以看出如果在定义类时没有显式指定它的父类则这些类默认的父类是 object 类。 Python 还为所有类都提供了一个 __subclasses__() 方法通过该方法可以查看该类的所有直接子类该方法返回该类的所有子类组成的列表。例如在上面程序中增加如下两行 print(类A的所有子类:, A.__subclasses__()) print(类B的所有子类:, B.__subclasses__()) 运行上面代码可以看到如下输出结果 类A的所有子类: [class __main__.C] 类B的所有子类: [class __main__.C] 八、Python __call__()方法详解版 本节再介绍 Python 类中一个非常特殊的实例方法即 __call__()。该方法的功能类似于在类中重载 () 运算符使得类实例对象可以像调用普通函数那样以“对象名()”的形式使用。 举个例子 class CLanguage:# 定义__call__方法def __call__(self,name,add):print(调用__call__()方法,name,add)clangs CLanguage() clangs(C语言中文网,http://c.biancheng.net) 程序执行结果为 调用__call__()方法 C语言中文网 http://c.biancheng.net 可以看到通过在 CLanguage 类中实现 __call__() 方法使的 clangs 实例对象变为了可调用对象。 Python 中凡是可以将 () 直接应用到自身并执行都称为可调用对象。可调用对象包括自定义的函数、Python 内置函数以及本节所讲的类实例对象。 对于可调用对象实际上“名称()”可以理解为是“名称.__call__()”的简写。仍以上面程序中定义的 clangs 实例对象为例其最后一行代码还可以改写为如下形式 clangs.__call__(C语言中文网,http://c.biancheng.net) 运行程序会发现其运行结果和之前完全相同。 这里再举一个自定义函数的例子例如 def say():print(Python教程http://c.biancheng.net/python) say() say.__call__() 程序执行结果为 Python教程http://c.biancheng.net/python Python教程http://c.biancheng.net/python 不仅如此类中的实例方法也有以上 2 种调用方式这里不再举例有兴趣的读者可自行编写代码尝试。 用 __call__() 弥补 hasattr() 函数的短板 前面章节介绍了 hasattr() 函数的用法该函数的功能是查找类的实例对象中是否包含指定名称的属性或者方法但该函数有一个缺陷即它无法判断该指定的名称到底是类属性还是类方法。 要解决这个问题我们可以借助可调用对象的概念。要知道类实例对象包含的方法其实也属于可调用对象但类属性却不是。举个例子 class CLanguage:def __init__ (self):self.name C语言中文网self.add http://c.biancheng.netdef say(self):print(我正在学Python)clangs CLanguage() if hasattr(clangs,name):print(hasattr(clangs.name,__call__)) print(**********) if hasattr(clangs,say):print(hasattr(clangs.say,__call__)) 程序执行结果为 False ********** True 可以看到由于 name 是类属性它没有以 __call__ 为名的 __call__() 方法而 say 是类方法它是可调用对象因此它有 __call__() 方法。 九、什么是运算符重载Python可重载运算符有哪些 前面章节介绍了 Python 中的各个序列类型每个类型都有其独特的操作方法例如列表类型支持直接做加法操作实现添加元素的功能字符串类型支持直接做加法实现字符串的拼接功能也就是说同样的运算符对于不同序列类型的意义是不一样的这是怎么做到的呢 其实在 Python 内部每种序列类型都是 Python 的一个类例如列表是 list 类字典是 dict 类等这些序列类的内部使用了一个叫作“重载运算符”的技术来实现不同运算符所对应的操作。 所谓重载运算符指的是在类中定义并实现一个与运算符对应的处理方法这样当类对象在进行运算符操作时系统就会调用类中相应的方法来处理。 这里给大家举一个与重载运算符相关的实例 class MyClass: #自定义一个类def __init__(self, name , age): #定义该类的初始化函数self.name name #将传入的参数值赋值给成员交量self.age agedef __str__(self): #用于将值转化为字符串形式等同于 str(obj)return name:self.name;age:str(self.age)__repr__ __str__ #转化为供解释器读取的形式def __lt__(self, record): #重载 selfrecord 运算符if self.age record.age:return Trueelse:return Falsedef __add__(self, record): #重载 号运算符return MyClass(self.name, self.agerecord.age)myc MyClass(Anna, 42) #实例化一个对象 Anna并为其初始化 mycl MyClass(Gary, 23) #实例化一个对象 Gary并为其初始化 print(repr(myc)) #格式化对象 myc print(myc) #解释器读取对象 myc调用 repr print (str (myc)) #格式化对象 myc 输出name:Anna;age:42 print(myc mycl) #比较 mycmycl 的结果输出 False print (mycmycl) #进行两个 MyClass 对象的相加运算输出 name:Anna;age:65 输出结果为 name:Anna;age:42 name:Anna;age:42 name:Anna;age:42 False name:Anna;age:65 这个例子中MyClass 类中重载了 repr、str、、 运算符并用 MyClass 实例化了两个对象 myc 和 mycl。 通过将 myc 进行 repr、str 运算从输出结果中可以看到程序调用了重载的操作符方法 __repr__ 和 __str__。而令 myc 和 mycl 进行 号的比较运算以及加法运算从输出结果中可以看出程序调用了重载 号的方法 __lt__ 和 __add__ 方法。 那么Python 类支持对哪些方法进行重载呢这个给大家提供一个表格表 1列出了 Python 中常用的可重载的运算符以及各自的含义。 表 1 Python 常用重载运算符 重载运算符含义__new__创建类在 __init__ 之前创建对象__init__类的构造函数其功能是创建类对象时做初始化工作。__del__ 析构函数其功能是销毁对象时进行回收资源的操作__add__加法运算符 当类对象 X 做例如 XY 或者 XY 等操作内部会调用此方法。但如果类中对 __iadd__ 方法进行了重载则类对象 X 在做 XY 类似操作时会优先选择调用 __iadd__ 方法。__radd__当类对象 X 做类似 YX 的运算时会调用此方法。__iadd__重载 运算符也就是说当类对象 X 做类似 XY 的操作时会调用此方法。__or__“或”运算符 |如果没有重载 __ior__则在类似 X|Y、X|Y 这样的语句中“或”符号生效__repr____str__格式转换方法分别对应函数 repr(X)、str(X)__call__函数调用类似于 X(*args, **kwargs) 语句__getattr__点号运算用来获取类属性__setattr__属性赋值语句类似于 X.anyvalue__delattr__删除属性类似于 del X.any__getattribute__获取属性类似于 X.any__getitem__索引运算类似于 X[key]X[i:j]__setitem__索引赋值语句类似于 X[key], X[i:j]sequence__delitem__ 索引和分片删除__get__, __set__, __delete__描述符属性类似于 X.attrX.attrvaluedel X.attr__len__ 计算长度类似于 len(X)__lt____gt____le____ge____eq____ne__ 比较分别对应于 、、、、、! 运算符。__iter____next__迭代环境下生成迭代器与取下一条类似于 Iiter(X) 和 next()__contains__成员关系测试类似于 item in X__index__ 整数值类似于 hex(X)bin(X)oct(X)__enter____exit__在对类对象执行类似 with obj as var 的操作之前会先调用 __enter__ 方法其结果会传给 var在最终结束该操作之前会调用 __exit__ 方法常用于做一些清理、扫尾的工作 十、Python重载运算符实现自定义序列 除了前面章节介绍的几个类特殊方法方法名以双下划线__开头和结尾在 Python 类中我们还可以通过重写几个特殊方法实现自定义一个序列类。表 1 列出了和自定义序列类有关的几个特殊方法。 表 1 和序列相关的特殊方法 方法名功能__len__(self)返回序列类中存储元素的个数。__contains__(self, value)判断当前序列中是否包含 value 这个指定元素。__getitem__(self, key)通过指定的 key键返回对应的 value值。__setitem__(self, key)修改指定 key键对应的 value值。__delitem__(self, key)删除指定键值对。 注意在对表 1 中的这些特殊方法进行重写时在实现其基础功能的基础上还可以根据实际情况对各个方法的具体实现进行适当调整。以 __setitem__() 方法为例当在序列中未找到指定 key 的情况下该方法可以报错当然也可以将此键值对添加到当前序列中。 另外值得一提的是在实现自定义序列类时并不是必须重写表 1 中全部的特殊方法。如果该自定义序列是一个不可变序列即序列中的元素不能做修改则无需重写 __setitem__() 和 __delitem__() 方法反之如果该自定义序列是一个可变序列可以重写以上 5 个特殊方法。 下面程序实现了一个比较简单的序列类这是一个字典类其特点是只能存储 int 类型的元素 class IntDic: def __init__(self):# 用于存储数据的字典self.__date {}def __len__(self):return len(list(self.__date.values()))def __getitem__(self, key):# 如果在self.__changed中找到已经修改后的数据if key in self.__date :return self.__date[key]return Nonedef __setitem__(self, key, value):#判断value是否为整数if not isinstance(value, int):raise TypeError(必须是整数)#修改现有 key 对应的 value 值或者直接添加self.__date[key] valuedef __delitem__(self, key):if key in self.__date : del self.__date[key] dic IntDic() #输出序列中元素的个数调用 __len__() 方法 print(len(dic)) #向序列中添加元素调用 __setitem__() 方法 dic[a] 1 dic[b] 2print(len(dic)) dic[a] 3 dic[c] 4 print(dic[a]) #删除指定元素调用 __delitem__() 方法 del dic[a] print(dic[a]) print(len(dic)) 程序执行结果为 0 2 3 None 2 十一、什么是迭代器Python迭代器及其用法 前面章节中已经对列表list、元组tuple、字典dict、集合set这些序列式容器做了详细的介绍。值得一提的是这些序列式容器有一个共同的特性它们都支持使用 for 循环遍历存储的元素都是可迭代的因此它们又有一个别称即迭代器。 从字面来理解迭代器指的就是支持迭代的容器更确切的说是支持迭代的容器类对象这里的容器可以是列表、元组等这些 Python 提供的基础容器也可以是自定义的容器类对象只要该容器支持迭代即可。 《Python实现自定义序列》一节中已经学会了如何自定义一个序列类但该序列类对象并不支持迭代因此还不能称之为迭代器。如果要自定义实现一个迭代器则类中必须实现如下 2 个方法 __next__(self)返回容器的下一个元素。__iter__(self)该方法返回一个迭代器iterator。 例如下面程序自定义了一个简易的列表容器迭代器支持迭代 class listDemo:def __init__(self):self.__date[]self.__step 0def __next__(self):if self.__step 0:raise StopIterationself.__step - 1#返回下一个元素return self.__date[self.__step]def __iter__(self):#实例对象本身就是迭代器对象因此直接返回 self 即可return self#添加元素def __setitem__(self,key,value):self.__date.insert(key,value)self.__step 1 mylist listDemo() mylist[0]1 mylist[1]2 for i in mylist:print (i) 程序执行结果为 2 1 除此之外Python 内置的 iter() 函数也会返回一个迭代器该函数的语法格式如下 iter(obj[, sentinel]) 其中obj 必须是一个可迭代的容器对象而 sentinel 作为可选参数如果使用此参数要求 obj 必须是一个可调用对象具体功能后面会讲。 可调用对象指的是该类的实例对象可以像函数那样直接以“对象名()”的形式被使用。通过在类中添加 __call__() 方法就可以将该类的实例对象编程可调用对象。有关 __call__() 方法可阅读《Python __call__()》做详细了解。 我们常用的是仅有 1 个参数的 iter() 函数通过传入一个可迭代的容器对象我们可以获得一个迭代器通过调用该迭代器中的 __next__() 方法即可实现迭代。例如 # 将列表转换为迭代器 myIter iter([1, 2, 3]) # 依次获取迭代器的下一个元素 print(myIter.__next__()) print(myIter.__next__()) print(myIter.__next__()) print(myIter.__next__()) 运行结果为 1 2 3 Traceback (most recent call last):File C:\Users\mengma\Desktop\demo.py, line 7, in moduleprint(myIter.__next__()) StopIteration 另外也可以使用 next() 内置函数来迭代即 next(myIter)和 __next__() 方法是完全一样的。 从程序的执行结果可以看出当迭代完存储的所有元素之后如果继续迭代则 __next__() 方法会抛出 StopIteration 异常。 这里介绍 iter() 函数第 2 个参数的作用如果使用该参数则要求第一个 obj 参数必须传入可调用对象可以不支持迭代这样当使用返回的迭代器调用 __next__() 方法时它会通过执行 obj() 调用 __call__() 方法如果该方法的返回值和第 2 个参数值相同则输出 StopInteration 异常反之则输出 __call__() 方法的返回值。 例如修改 listDemo 类如下所示 class listDemo:def __init__(self):self.__date[]self.__step 0def __setitem__(self,key,value):self.__date.insert(key,value)self.__step 1#是该类实例对象成为可调用对象def __call__(self):self.__step-1return self.__date[self.__step]mylist listDemo() mylist[0]1 mylist[1]2 #将 mylist 变为迭代器 a iter(mylist,1) print(a.__next__()) print(a.__next__()) 程序执行结果为 2 Traceback (most recent call last):File D:\python3.6\1.py, line 20, in moduleprint(a.__next__()) StopIteration 输出结果中之所以最终抛出 StopIteration 异常是因为这里原本要输出的元素 1 和 iter() 函数的第 2 个参数相同。 迭代器本身是一个底层的特性和概念在程序中并不常用但它为生成器这一更有趣的特性提供了基础。 十二、Python项目实战之迭代器实现字符串的逆序输出 《Python迭代器》一节已经对如何创建迭代器做了详细的介绍本节将利用迭代器完成对字符串的逆序操作。项目要求是这样的定义一个类要求在实现迭代器功能的基础上能够对用户输入的字符串做逆序输出操作。实现思路是这样的自定义一个类并重载其 __init__() 初始化方法实现为自身私有成员赋值。同时重载 __iter__() 和 __next__() 方法使其具有迭代器功能。在此基础上如果想实现对用户输入的字符串进行逆序输出就需要在 __next__() 方法中实现从后往前返回字符。 实现代码如下 class Reverse:def __init__(self, string):self.__string stringself.__index len(string)def __iter__(self):return selfdef __next__(self):self.__index - 1return self.__string[self.__index] revstr Reverse(Python) for c in revstr:print(c,end ) 运行结果为 n o h t y P n o h t y P Traceback (most recent call last):File C:\Users\mengma\Desktop\demo.py, line 11, in modulefor c in revstr:File C:\Users\mengma\Desktop\demo.py, line 9, in __next__return self.__string[self.__index] IndexError: string index out of range 可以看到上面程序在逆序输出两遍python的同时Python解释器报出 IndexError 错误这是什么原因呢 很简单因为程序没有设置遍历的终止条件换句话说没有对 __index 私有变量的值对限制这里 __index 的取值范围应为-len(self.__index), len(self.__index)这也是导致上面程序运行结果的根本原因。 编写迭代器最容易忽视的一个环节就是在自定义类中加入对循环结束的判断并抛出 StopIteration 异常只有这么做了for 循环才会接收到 StopIteration 异常并当做终止信号来结束循环。 所以我们需要对上面程序做适当的调整如下所示 class Reverse:def __init__(self, string):self.__string stringself.__index len(string)def __iter__(self):return selfdef __next__(self):if self.__index 0:raise(StopIteration)self.__index - 1return self.__string[self.__index] revstr Reverse(Python) for c in revstr:print(c,end ) 运行结果为 n o h t y P 十三、Python生成器详解 前面章节中已经详细介绍了什么是迭代器。生成器本质上也是迭代器不过它比较特殊。以 list 容器为例在使用该容器迭代一组数据时必须事先将所有数据存储到容器中才能开始迭代而生成器却不同它可以实现在迭代的同时生成元素。 也就是说对于可以用某种算法推算得到的多个数据生成器并不会一次性生成它们而是什么时候需要才什么时候生成。 不仅如此生成器的创建方式也比迭代器简单很多大体分为以下 2 步 定义一个以 yield 关键字标识返回值的函数调用刚刚创建的函数即可创建一个生成器。 举个例子 def intNum():print(开始执行)for i in range(5):yield iprint(继续执行) num intNum() 由此我们就成功创建了一个 num 生成器对象。显然和普通函数不同intNum() 函数的返回值用的是 yield 关键字而不是 return 关键字此类函数又成为生成器函数。 和 return 相比yield 除了可以返回相应的值还有一个更重要的功能即每当程序执行完该语句时程序就会暂停执行。不仅如此即便调用生成器函数Python 解释器也不会执行函数中的代码它只会返回一个生成器对象。 要想使生成器函数得以执行或者想使执行完 yield 语句立即暂停的程序得以继续执行有以下 2 种方式 通过生成器上面程序中的 num调用 next() 内置函数或者 __next__() 方法通过 for 循环遍历生成器。 例如在上面程序的基础上添加如下语句 #调用 next() 内置函数 print(next(num)) #调用 __next__() 方法 print(num.__next__()) #通过for循环遍历生成器 for i in num:print(i) 程序执行结果为 开始执行 0 继续执行 1 继续执行 2 继续执行 3 继续执行 4 继续执行 这里有必要给读者分析一个程序的执行流程 1) 首先在创建有 num 生成器的前提下通过其调用 next() 内置函数会使 Python 解释器开始执行 intNum() 生成器函数中的代码因此会输出“开始执行”程序会一直执行到yield i而此时的 i0因此 Python 解释器输出“0”。由于受到 yield 的影响程序会在此处暂停。 2) 然后我们使用 num 生成器调用 __next__() 方法该方法的作用和 next() 函数完全相同事实上next() 函数的底层执行的也是 __next__() 方法它会是程序继续执行即输出“继续执行”程序又会执行到yield i此时 i1因此输出“1”然后程序暂停。 3) 最后我们使用 for 循环遍历 num 生成器之所以能这么做是因为 for 循环底层会不断地调用 next() 函数使暂停的程序继续执行因此会输出后续的结果。 注意在 Python 2.x 版本中不能使用 __next__() 方法可以使用 next() 内置函数另外生成器还有 next() 方法即以 num.next() 的方式调用。 除此之外还可以使用 list() 函数和 tuple() 函数直接将生成器能生成的所有值存储成列表或者元组的形式。例如 num intNum() print(list(num))num intNum() print(tuple(num)) 程序执行结果为 开始执行 继续执行 继续执行 继续执行 继续执行 继续执行 [0, 1, 2, 3, 4] 开始执行 继续执行 继续执行 继续执行 继续执行 继续执行 (0, 1, 2, 3, 4) 通过输出结果可以判断出list() 和 tuple() 底层实现和 for 循环的遍历过程是类似的。 相比迭代器生成器最明显的优势就是节省内存空间即它不会一次性生成所有的数据而是什么时候需要什么时候生成。 十四、Python生成器sendclosethrow方法详解 《Python生成器》一节中详细介绍了如何创建一个生成器以及生成器的基础用法。本节将在其基础上继续讲解和生成器有关的一些方法。 1、Python生成器send()方法 我们知道通过调用 next() 或者 __next__() 方法可以实现从外界控制生成器的执行。除此之外通过 send() 方法还可以向生成器中传值。 值得一提的是send() 方法可带一个参数也可以不带任何参数用 None 表示。其中当使用不带参数的 send() 方法时它和 next() 函数的功能完全相同。例如 def intNum():print(开始执行)for i in range(5):yield iprint(继续执行) num intNum() print(num.send(None)) print(num.send(None)) 程序执行结果为 开始执行 0 继续执行 1 注意虽然 send(None) 的功能是 next() 完全相同但更推荐使用 next()不推荐使用 send(None)。 这里重点讲解一些带参数的 send(value) 的用法其具备 next() 函数的部分功能即将暂停在 yield 语句出的程序继续执行但与此同时该函数还会将 value 值作为 yield 语句返回值赋值给接收者。 注意带参数的 send(value) 无法启动执行生成器函数。也就是说程序中第一次使用生成器调用 next() 或者 send() 函数时不能使用带参数的 send() 函数。 举个例子 def foo():bar_a yield hellobar_b yield bar_ayield bar_bf foo() print(f.send(None)) print(f.send(C语言中文网)) print(f.send(http://c.biancheng.net)) 分析一下此程序的执行流程 1) 首先构建生成器函数并利用器创建生成器对象f 。 2) 使用生成器 f 调用无参的 send() 函数其功能和 next() 函数完全相同因此开始执行生成器函数即执行到第一个 yield hello 语句该语句会返回 hello 字符串然后程序停止到此处注意此时还未执行对 bar_a 的赋值操作。 3) 下面开始使用生成器 f 调用有参的 send() 函数首先它会将暂停的程序开启同时还会将其参数“C语言中文网”赋值给当前 yield 语句的接收者也就是 bar_a 变量。程序一直执行完 yield bar_a 再次暂停因此会输出“C语言中文网”。 4 最后依旧是调用有参的 send() 函数同样它会启动餐厅的程序同时将参数“http://c.biancheng.net”传给 bar_b然后执行完 yield bar_b 后输出 http://c.biancheng.net程序执行再次暂停。 因此该程序的执行结果为 hello C语言中文网 http://c.biancheng.net 2、Python生成器close()方法 当程序在生成器函数中遇到 yield 语句暂停运行时此时如果调用 close() 方法会阻止生成器函数继续执行该函数会在程序停止运行的位置抛出 GeneratorExit 异常。 举个例子 def foo():try:yield 1except GeneratorExit:print(捕获到 GeneratorExit) f foo() print(next(f)) f.close() 程序执行结果为 1 捕获到 GeneratorExit 注意虽然通过捕获 GeneratorExit 异常可以继续执行生成器函数中剩余的代码带这部分代码中不能再包含 yield 语句否则程序会抛出 RuntimeError 异常。例如 def foo():try:yield 1except GeneratorExit:print(捕获到 GeneratorExit)yield 2 #抛出 RuntimeError 异常f foo() print(next(f)) f.close() 程序执行结果为 1 捕获到 GeneratorExit Traceback (most recent call last):File D:\python3.6\1.py, line 10, in modulef.close() RuntimeError: generator ignored GeneratorExit 另外生成器函数一旦使用 close() 函数停止运行后续将无法再调用 next() 函数或者 __next__() 方法启动执行否则会抛出 StopIteration 异常。例如 def foo():yield c.biancheng.netprint(生成器停止执行)f foo() print(next(f)) #输出 c.biancheng.net f.close() next(f) #原本应输出生成器停止执行 程序执行结果为 c.biancheng.net Traceback (most recent call last):File D:\python3.6\1.py, line 8, in modulenext(f) #原本应输出生成器停止执行 StopIteration 3、Python生成器throw()方法 生成器 throw() 方法的功能是在生成器函数执行暂停处抛出一个指定的异常之后程序会继续执行生成器函数中后续的代码直到遇到下一个 yield 语句。需要注意的是如果到剩余代码执行完毕没有遇到下一个 yield 语句则程序会抛出 StopIteration 异常。 举个例子 def foo():try:yield 1except ValueError:print(捕获到 ValueError)f foo() print(next(f)) f.throw(ValueError) 程序执行结果为 1 捕获到 ValueError Traceback (most recent call last):File D:\python3.6\1.py, line 9, in modulef.throw(ValueError) StopIteration 显然一开始生成器函数在 yield 1 处暂停执行当执行 throw() 方法时它会先抛出 ValueError 异常然后继续执行后续代码找到下一个 yield 语句该程序中由于后续不再有 yield 语句因此程序执行到最后会抛出一个 StopIteration 异常。 十五、Python 函数装饰器及用法超级详细 前面章节中我们已经讲解了 Python 内置的 3 种函数装饰器分别是 staticmethod、classmethod 和 property其中 staticmethod()、classmethod() 和 property() 都是 Python 的内置函数。 那么函数装饰器的工作原理是怎样的呢假设用 funA() 函数装饰器去装饰 funB() 函数如下所示 #funA 作为装饰器函数 def funA(fn):#...fn() # 执行传入的fn参数#...return ...funA def funB():#... 实际上上面程序完全等价于下面的程序 def funA(fn):#...fn() # 执行传入的fn参数#...return ...def funB():#...funB funA(funB) 通过比对以上 2 段程序不难发现使用函数装饰器 A() 去装饰另一个函数 B()其底层执行了如下 2 步操作 将 B 作为参数传给 A() 函数将 A() 函数执行完成的返回值反馈回  B。 举个实例 #funA 作为装饰器函数 def funA(fn):print(C语言中文网)fn() # 执行传入的fn参数print(http://c.biancheng.net)return 装饰器函数的返回值funA def funB():print(学习 Python) 程序执行流程为 C语言中文网 学习 Python http://c.biancheng.net 在此基础上如果在程序末尾添加如下语句 print(funB) 其输出结果为 装饰器函数的返回值 显然被“函数”修饰的函数不再是原来的函数而是被替换成一个新的东西取决于装饰器的返回值即如果装饰器函数的返回值为普通变量那么被修饰的函数名就变成了变量名同样如果装饰器返回的是一个函数的名称那么被修饰的函数名依然表示一个函数。 实际上所谓函数装饰器就是通过装饰器函数在不修改原函数的前提下来对函数的功能进行合理的扩充。 1、带参数的函数装饰器 在分析 funA() 函数装饰器和 funB() 函数的关系时细心的读者可能会发现一个问题即当 funB() 函数无参数时可以直接将 funB 作为 funA() 的参数传入。但是如果被修饰的函数本身带有参数那应该如何传值呢 比较简单的解决方法就是在函数装饰器中嵌套一个函数该函数带有的参数个数和被装饰器修饰的函数相同。例如 def funA(fn):# 定义一个嵌套函数def say(arc):print(Python教程:,arc)return sayfunA def funB(arc):print(funB():, a) funB(http://c.biancheng.net/python) 程序执行结果为 Python教程: http://c.biancheng.net/python 这里有必要给读者分析一下这个程序其实它和如下程序是等价的 def funA(fn):# 定义一个嵌套函数def say(arc):print(Python教程:,arc)return saydef funB(arc):print(funB():, a)funB funA(funB) funB(http://c.biancheng.net/python) 如果运行此程序会发现它的输出结果和上面程序相同。 显然通过 funB() 函数被装饰器 funA() 修饰funB 就被赋值为 say。这意味着虽然我们在程序显式调用的是 funB() 函数但其实执行的是装饰器嵌套的 say() 函数。 但还有一个问题需要解决即如果当前程序中有多个≥ 2函数被同一个装饰器函数修饰这些函数带有的参数个数并不相等怎么办呢 最简单的解决方式是用 *args 和 **kwargs 作为装饰器内部嵌套函数的参数*args 和 **kwargs 表示接受任意数量和类型的参数。举个例子 def funA(fn):# 定义一个嵌套函数def say(*args,**kwargs):fn(*args,**kwargs)return sayfunA def funB(arc):print(C语言中文网,arc)funA def other_funB(name,arc):print(name,arc) funB(http://c.biancheng.net) other_funB(Python教程,http://c.biancheng.net/python) 运行结果为 C语言中文网 http://c.biancheng.net Python教程 http://c.biancheng.net/python 2、函数装饰器可以嵌套 上面示例中都是使用一个装饰器的情况但实际上Python 也支持多个装饰器比如 funA funB funC def fun():#... 上面程序的执行顺序是里到外所以它等效于下面这行代码 fun funA( funB ( funC (fun) ) ) 这里不再给出具体实例有兴趣的读者可自行编写程序进行测试。 十六、Python装饰器的应用场景 前面章节已经讲解了装饰器的基本概念及用法本节将结合实际工作中的几个例子带读者加深对它的理解。 1、装饰器用于身份认证 首先是最常见的身份认证的应用。这个很容易理解举个最常见的例子大家登录微信需要输入用户名密码然后点击确认这样服务器端便会查询你的用户名是否存在、是否和密码匹配等等。如果认证通过就可以顺利登录反之则提示你登录失败。 再比如一些网站你不登录也可以浏览内容但如果你想要发布文章或留言在点击发布时服务器端便会查询你是否登录。如果没有登录就不允许这项操作等等。 如下是一个实现身份认证的简单示例 import functoolsdef authenticate(func):functools.wraps(func)def wrapper(*args, **kwargs):request args[0]# 如果用户处于登录状态if check_user_logged_in(request):# 执行函数 post_comment()return func(*args, **kwargs) else:raise Exception(Authentication failed)return wrapperauthenticate def post_comment(request, ...)... 注意对于函数来说它也有自己的一些属性例如 __name__ 属性代码中 functools.wraps(func) 也是一个装饰器如果不使用它则 post_comment.__name__ 的值为 wrapper。而使用它之后则 post_comment.__name__ 的值依然为 post_comment。 上面这段代码中定义了装饰器 authenticate函数 post_comment() 则表示发表用户对某篇文章的评论每次调用这个函数前都会先检查用户是否处于登录状态如果是登录状态则允许这项操作如果没有登录则不允许。 2、装饰器用于日志记录 日志记录同样是很常见的一个案例。在实际工作中如果你怀疑某些函数的耗时过长导致整个系统的延迟增加想在线上测试某些函数的执行时间那么装饰器就是一种很常用的手段。 我们通常用下面的方法来表示 import time import functoolsdef log_execution_time(func):functools.wraps(func)def wrapper(*args, **kwargs):start time.perf_counter()res func(*args, **kwargs)end time.perf_counter()print({} took {} ms.format(func.__name__, (end - start) * 1000))return resreturn wrapperlog_execution_time def calculate_similarity(items):... 这里装饰器 log_execution_time 记录某个函数的运行时间并返回其执行结果。如果你想计算任何函数的执行时间在这个函数上方加上log_execution_time即可。 3、装饰器用于输入合理性检查 在大型公司的机器学习框架中调用机器集群进行模型训练前往往会用装饰器对其输入往往是很长的 json 文件进行合理性检查。这样就可以大大避免输入不正确对机器造成的巨大开销。 它的写法往往是下面的格式 import functoolsdef validation_check(input):functools.wraps(func)def wrapper(*args, **kwargs):... # 检查输入是否合法validation_check def neural_network_training(param1, param2, ...):... 其实在工作中很多情况下都会出现输入不合理的现象。因为我们调用的训练模型往往很复杂输入的文件有成千上万行很多时候确实也很难发现。 试想一下如果没有输入的合理性检查很容易出现“模型训练了好几个小时后系统却报错说输入的一个参数不对成果付之一炬”的现象。这样的“惨案”大大减缓了开发效率也对机器资源造成了巨大浪费。 4、缓存装饰器 关于缓存装饰器的用法其实十分常见这里以 Python 内置的 LRU cache 为例来说明。 LRU cache在 Python 中的表示形式是 lru_cache。lru_cache 会缓存进程中的函数参数和结果当缓存满了以后会删除最近最久未使用的数据。 正确使用缓存装饰器往往能极大地提高程序运行效率。举个例子大型公司服务器端的代码中往往存在很多关于设备的检查比如使用的设备是安卓还是 iPhone版本号是多少。这其中的一个原因就是一些新的功能往往只在某些特定的手机系统或版本上才有比如 Android v200。 这样一来我们通常使用缓存装饰器来包裹这些检查函数避免其被反复调用进而提高程序运行效率比如写成下面这样 lru_cache def check(param1, param2, ...) # 检查用户设备类型版本号等等 ...
http://www.zqtcl.cn/news/281978/

相关文章:

  • 网站建设分金手指专业十七wordpress 审核
  • 怎么欣赏一个网站设计图网站传送门怎么做
  • 网站有什么组成上海做推广网站
  • 网站上传大马后怎么做管理咨询公司口号
  • 网站集约整合建设交流雅虎网站提交入口
  • 网站安全建设必要性网站登录页面
  • 鄂州网站推广做区块链在哪个网站
  • 网站什么内容网站安全性设计
  • 免费动态域名申请seo发布网站
  • 软件毕设代做网站广告设计公司资质
  • 织梦网站模板如何安装网页设计教程心得体会
  • 网站开发 男生网站二维码怎么做的
  • net网站开发教程网站防御怎么做
  • 手机网站设计只选亿企邦哪个选项不属于网络营销的特点
  • 繁昌网站建设如何用易语言做网站
  • 电子商务网站建设财务分析建立网站方法
  • 大专学网站开发wordpress显示数据库请求
  • 诸暨网站制作设计公众号文章怎么导入到wordpress
  • 网站死链怎么办青岛网站制作企业
  • 已经有域名 怎么修改网站网站推广找客户
  • 网站的制作建站人增加网站流量
  • 向国旗致敬做时代新人网站广州网站建设公司排名
  • 阿里云域名怎么做网站对网站进行seo优化
  • 响应式网站建设合同11月将现新冠感染高峰
  • 做网站客户一般会问什么问题百度云网盘资源分享网站
  • 网站设计中超链接怎么做艺术设计
  • 卡盟网站建设wordpress优化代码
  • 做网站需要什么技术员商城型网站开发网站建设
  • discuz做地方门户网站网站大全免费完整版
  • 莆田人做的网站一天赚2000加微信