zoukankan      html  css  js  c++  java
  • 类特殊成员(属性和方法)

    Python下划线命名模式 - 小结

    以下是一个简短的小结,即"速查表",罗列了我在本文中谈到的五种Python下划线模式的含义:

    因为python中所有类默认继承object类。而object类提供了了很多原始的内建属性和方法,所以用户自定义的类在Python中也会继承这些内建属性。可以使用dir()函数可以查看,虽然python提供了很多内建属性但实际开发中常用的不多。而很多系统提供的内建属性实际开发中用户都需要重写后才会使用。对于python来说,属性或者函数都可以被理解成一个属性

    [root@kube method]# cat demo2.py 
    class Person:
        pass
    
    print(dir(Person))   #使用dir 函数查看内建属性
    [root@kube method]# py demo2.py 
    ['__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__']
    [root@kube method]# 

    1.常用内建属性:__init__和__new__

    1.__init__方法使用与功能1.用来构造初始化函数,用来给类的实例进行初始化属性,所以可以不需要返回值
      2.在创建类的实例时系统自动调用
      3.自定义类如果不定义的话,默认调用父类object的,同理继承也是,子类若无,调用父类,若有,调用自己的
    class Student(object):
        def __init__(self,name):
            self.name = name
            print("这是__init__方法")
     
    s = Student("tom")  
    '''
    这是__init__方法
    2.__new__方法使用与功能
      1.__new__功能:用所给类创建一个对象,并且返回这个对象。
      2.因为是给类创建实例,所以至少传一个参数cls,参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供
      3.在类实例化时内部创建类实例的函数,并且返回这个实例,所以它是类实例时最先被调用的方法,一般不要人为定义该方法。
      4.因为要创建实例返回实例,所以要有返回值。return父类__new__出来的实例,或者直接是object的__new__出来的实例   
     
     
    class Student(object):
        def __init__(self,name):
            self.name = name
            print("这是__init__方法")
     
        def __new__(cls, *args, **kwargs):
            print("这是__new__方法")
            return object.__new__(cls)
     
    s = Student("tom")
    '''结果如下:注意__new__的执行顺序在__init__之前
    这是__new__方法
    这是_init__方法
    '''
     
    3.__init__和__new__使用的联系
      1.__init__第一个参数是self,表示需要初始的实例,由python解释器自动传入,而这个实例就是这个__new__返回的实例
    #那么理解就是在类实例化的过程中需要先通过__new__ 进行实列化返回一个实例,然后这个实例再有 __init__进行实例初始化
    [root@kube method]# cat demo5.py 
    #coding:utf-8
    
    class test:
        def __new__(cls,name,age):    #先通过__new__ 内置属性定义实列,然后交给 init 初始化实例,如果__new__ 没有完成后面就不会进行
            if 0 < age < 50:
                return object.__new__(cls)
            else:
                return None
    
        def __init__(self,name,age):
            
            print('__init__ age:', age)
    
    a = test('aaa',30)
    b = test('aaifffa',100)
    [root@kube method]# py demo5.py 
    __init__ age: 30
    [root@kube method]# 
    3.__init__和__new__使用的联系
      1.__init__第一个参数是self,表示需要初始的实例,由python解释器自动传入,而这个实例就是这个__new__返回的实例
      2.然后 __init__在__new__的基础上可以完成一些其它初始化的动作
     
    class Student(object):
        def __init__(self,name):
            self.name = name
            print("这是__init__方法")
     
        def __new__(cls, *args, **kwargs):
            print("这是__new__方法")
            id =object.__new__(cls)
            print(id) #打印这个__new__创建并返回的实例在内存中的地址
            return id
    s1 = Student("JACK")
    print(s1)
    '''
    这是__new__方法
    <__main__.Student object at 0x000001EC6C8C8748>
    这是__init__方法
    <__main__.Student object at 0x000001EC6C8C8748>
    '''
     

    Python __repr__()方法:显示属性

    [root@kube method]# cat demo6.py 
    class Item:
        def __init__ (self, name, price):
            self.name = name
            self.price = price
    # 创建一个Item对象,将之赋给im变量
    im = Item('鼠标', 29.8)
    # 打印im所引用的Item对象
    print(im)
    print(im.__repr__)
    [root@kube method]# py demo6.py 
    <__main__.Item object at 0x7f7628881290>
    <method-wrapper '__repr__' of Item object at 0x7f7628881290>
    [root@kube method]# 
    [root@kube method]# cat demo7.py 
    #coding:utf-8
    
    class Apple:
        def __init__(self,color,weight):
            self.color = color
            self.weight = weight
        def __repr__(self):
            return 'Apple class'+self.color+str(self.weight)
    
    a = Apple('red',8.88)
    print(a)
    [root@kube method]# py demo7.py 
    Apple classred8.88
    [root@kube method]# 
    #__repr__() 是一个非常特殊的方法,它是一个“自我描述”的方法,该方法通常用于实现这样一个功能:当程序员直接打印该对象时,系统将会输出该对象的“自我描述”信息,用来告诉外界该对象具有的状态信息。

    Python __del__方法:销毁对象

    __init__() 方法对应的是 __del__() 方法,__init__() 方法用于初始化 Python 对象,而 __del__() 则用于销毁 Python 对象,即在任何 Python 对象将要被系统回收之时,系统都会自动调用该对象的 __del__() 方法。
    
    当程序不再需要一个 Python 对象时,系统必须把该对象所占用的内存空间释放出来,这个过程被称为垃圾回收(GC,Garbage Collector),Python 会自动回收所有对象所占用的内存空间,因此开发者无须关心对象垃圾回收的过程。
    Python 采用自动引用计数(ARC)方式来回收对象所占用的空间,当程序中有一个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 1;当程序中有两个变量引用该 Python 对象时,Python 会自动保证该对象引用计数为 2,依此类推,如果一个对象的引用计数变成了 0,则说明程序中不再有变量引用该对象,表明程序不再需要该对象,因此 Python 就会回收该对象。
    
    大部分时候,Python 的 ARC 都能准确、高效地回收系统中的每个对象。但如果系统中出现循环引用的情况,比如对象 a 持有一个实例变量引用对象 b,而对象 b 又持有一个实例变量引用对象 a,此时两个对象的引用计数都是 1,而实际上程序已经不再有变量引用它们,系统应该回收它们,此时 Python 的垃圾回收器就可能没那么快,要等专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环。
    
    当一个对象被垃圾回收时,Python 就会自动调用该对象的 __del__ 方法。需要说明的是,不要以为对一个变量执行 del 操作,该变量所引用的对象就会被回收,只有当对象的引用计数变成 0 时,该对象才会被回收。因此,如果一个对象有多个变量引用它,那么 del 其中一个变量是不会回收该对象的。
    [root@kube method]# cat demo8.py 
    class Item:
        def __init__ (self, name, price):
            self.name = name
            self.price = price
        # 定义析构函数
        def __del__ (self):
            print('del删除对象')
    # 创建一个Item对象,将之赋给im变量
    im = Item('鼠标', 29.8)
    x = im   #
    # 打印im所引用的Item对象
    del im
    print('--------------')
    [root@kube method]# py demo8.py 
    --------------
    del删除对象
    [root@kube method]# 

    从上面程序的输出结果可以看到,del im 执行之后,程序并没有回收 Item 对象,只有等到程序执行将要结束时(系统必须回收所有对象),系统才会回收 Item 对象。

    Python __dir__用法:列出对象的所有属性(方法)名

    对象的 __dir__ 方法用于列出该对象内部的所有属性(包括方法)名,该方法将会返回包含所有属性(方法)名的序列。

    当程序对某个对象执行 dir(object) 函数时,实际上就是将该对象的 __dir__() 方法返回值进行排序,然后包装成列表。

    [root@kube method]# cat demo9.py 
    class Item:
        def __init__ (self, name, price):
            self.name = name
            self.price = price
        def info ():
            pass
    # 创建一个Item对象,将之赋给im变量
    im = Item('鼠标', 29.8)
    print(im.__dir__())  # 返回所有属性(包括方法)组成列表
    print('----------------------------------------------------------------')
    print(dir(im))  # 返回所有属性(包括方法)排序之后的列表
    [root@kube method]# py demo9.py 
    ['name', 'price', '__module__', '__init__', 'info', '__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__']
    ----------------------------------------------------------------
    ['__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__', 'info', 'name', 'price']
    [root@kube method]# 

    Python __dict__属性:查看对象内部所有属性名和属性值组成的字典

    __dict__ 属性用于查看对象内部存储的所有属性名和属性值组成的字典,通常程序直接使用该属性即可。

    程序使用 __dict__ 属性既可查看对象的所有内部状态,也可通过字典语法来访问或修改指定属性的值。例如如下程序:

    [root@kube method]# cat demo10.py 
    #coding:utf-8
    
    class Item:
        def __init__(self, name ,price):
            self.name = name
            self.price = price
    im = Item('computer',10000)
    
    print(im.__dict__)
    print(im.__dict__['name'])
    
    im.__dict__['name'] = '鼠标'
    print(im.__dict__['name'])
    
    [root@kube method]# py demo10.py 
    {'name': 'computer', 'price': 10000}
    computer
    鼠标
    [root@kube method]# 

    Python setattr()、getattr()、hasattr()函数用法详解

    在动态检查对象是否包含某些属性(包括方法〉相关的函数有如下几个:
    hasattr(obj, name):检查 obj 对象是否包含名为 name 的属性或方法。
    getattr(object, name[, default]):获取 object 对象中名为 name 的属性的属性值。
    setattr(obj, name, value,/):将obj 对象的 name 属性设为 value。
    
    下面程序示范了通过以上函数来动态操作 Python 对象的属性:
    [root@kube method]# cat demo11.py 
    class Comment:
        def __init__ (self, detail, view_times):
            self.detail = detail
            self.view_times = view_times
        def info ():
            print("一条简单的评论,内容是%s" % self.detail)
           
    c = Comment('疯狂Python讲义很不错', 20)
    # 判断是否包含指定的属性或方法
    print(hasattr(c, 'detail')) # True
    print(hasattr(c, 'view_times')) # True
    print(hasattr(c, 'info')) # True
    # 获取指定属性的属性值
    print(getattr(c, 'detail')) # '疯狂Python讲义很不错'
    print(getattr(c, 'view_times')) # 20
    # 由于info是方法,故下面代码会提示:name 'info' is not defined
    #print(getattr(c, info, '默认值'))
    # 为指定属性设置属性值
    setattr(c, 'detail', '天气不错')
    setattr(c, 'view_times', 32)
    # 输出重新设置后的属性值
    print(c.detail)
    print(c.view_times)
    [root@kube method]# py demo11.py 
    True
    True
    True
    疯狂Python讲义很不错
    20
    天气不错
    32
    [root@kube method]# 

    Python issubclass和isinstance函数:检查类型

    Python 提供了如下两个函数来检查类型:

    • issubclass(cls, class_or_tuple):检查 cls 是否为后一个类或元组包含的多个类中任意类的子类。
    • isinstance(obj, class_or_tuple):检查 obj 是否为后一个类或元组包含的多个类中任意类的对象。
    [root@kube method]# cat demo12.py 
    # 定义一个字符串
    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))
    [root@kube method]# py demo12.py 
    "Hello"是否是str类的实例:  True
    "Hello"是否是object类的实例:  True
    str是否是object类的子类:  True
    "Hello"是否是tuple类的实例:  False
    str是否是tuple类的子类:  False
    [2, 4]是否是list类的实例:  True
    [2, 4]是否是object类及其子类的实例:  True
    list是否是object类的子类:  True
    [2, 4]是否是tuple类及其子类的实例:  False
    list是否是tuple类的子类:  False
    [root@kube method]# 
    __bases__    和__subclasses__()
    [root@kube method]# cat demo13.py 
    #coding:utf-8
    
    class A:
        pass
    class B(A):
        pass
    class C(A):
        pass
    #类的 __bases__ 属性查看该类的直接父类
    print('类A 的所有父类:',A.__bases__)
    print('类B 的所有父类:',B.__bases__)
    print('类C 的所有父类:',C.__bases__)
    #类的 __subclasses_() 方法。查看该类的所有直接子类
    
    print('类A 的子类:',A.__subclasses__())
    print('类B 的子类:',B.__subclasses__())
    [root@kube method]# py demo13.py 
    类A 的所有父类: (<class 'object'>,)
    类B 的所有父类: (<class '__main__.A'>,)
    类C 的所有父类: (<class '__main__.A'>,)
    类A 的子类: [<class '__main__.B'>, <class '__main__.C'>]
    类B 的子类: []
    [root@kube method]# 

    Python __call__方法(详解版)

    前面章节中,我们用 hasattr() 函数判断指定属性(或方法)是否存在,但到底是属性还是方法,则需要进一步判断它是否可调用。程序可通过判断该属性(或方法)是否包含 __call__ 属性来确定它是否可调用

    [root@kube method]# cat demo14.py 
    #coding:utf-8
    
    class User:
        def __init__(self, name , passwd):
            self.name = name
            self.passwd = passwd
        def valid_login(self):
            print('验证%s 的登录' % self.name)
    
    u = User('jojo','123.com')
    #判断 u.name 是否包含__call__方法,及判断是够可调用
    print(hasattr(u.name,'__call__'))
    print(hasattr(u.passwd,'__call__'))
    print(hasattr(u.valid_login ,'__call__'))
    
    [root@kube method]# py demo14.py 
    False
    False
    True
    [root@kube method]# 

    实际上,一个函数(甚至对象)之所以能执行,关键就在于 __call__() 方法。实际上 x(arg1, arg2,...) 只是 x.__call__(arg1, arg2, ...) 的快捷写法,因此我们甚至可以为自定义类添加 __call__ 方法,从而使得该类的实例也变成可调用的。例如如下代码:

    [root@kube method]# cat demo15.py 
    # 定义Role类
    class Role:
        def __init__ (self, name):
            self.name = name
        # 定义__call__方法
        def __call__(self):
            print('执行Role对象')
    r = Role('管理员')
    # 直接调用Role对象,就是调用该对象的__call__方法
    r()
    [root@kube method]# py demo15.py 
    执行Role对象
    [root@kube method]# 

    __str__ 是一个类的内置方法,用于类的说明

    例如:

    [root@kube method]# cat demo16.py 
    class person(object):
    
        def __init__(self, id, name):
            self.id = id
            self.name = name
    
        def __str__(self):
            return "{}---{}".format(self.id, self.name)  # .format()格式化输出
    
    
    
    p = person(1, 'test1') 
    print(p)
    [root@kube method]# py demo16.py 
    1---test1
    [root@kube method]# 

     

    什么是运算符重载,Python可重载运算符有哪些?

    所谓重载运算符,指的是在类中定义并实现一个与运算符对应的处理方法,这样当类对象在进行运算符操作时,系统就会调用类中相应的方法来处理。

    表 1 Python 常用重载运算符
    重载运算符含义
    __new__ 创建类,在 __init__ 之前创建对象
    __init__ 类的构造函数,其功能是创建类对象时做初始化工作。
    __del__  析构函数,其功能是销毁对象时进行回收资源的操作
    __add__ 加法运算符 +,当类对象 X 做例如 X+Y 或者 X+=Y 等操作,内部会调用此方法。但如果类中对 __iadd__ 方法进行了重载,则类对象 X 在做 X+=Y 类似操作时,会优先选择调用 __iadd__ 方法。
    __radd__ 当类对象 X 做类似 Y+X 的运算时,会调用此方法。
    __iadd__ 重载 += 运算符,也就是说,当类对象 X 做类似 X+=Y 的操作时,会调用此方法。
    __or__ “或”运算符 |,如果没有重载 __ior__,则在类似 X|Y、X|=Y 这样的语句中,“或”符号生效
    __repr__,__str__ 格式转换方法,分别对应函数 repr(X)、str(X)
    __call__ 函数调用,类似于 X(*args, **kwargs) 语句
    __getattr__ 点号运算,用来获取类属性
    __setattr__ 属性赋值语句,类似于 X.any=value
    __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.attr,X.attr=value,del X.attr
    __len__  计算长度,类似于 len(X)
    __lt__,__gt__,__le__,__ge__,__eq__,__ne__  比较,分别对应于 <、>、<=、>=、=、!= 运算符。
    __iter__,__next__ 迭代环境下,生成迭代器与取下一条,类似于 I=iter(X) 和 next()
    __contains__ 成员关系测试,类似于 item in X
    __index__  整数值,类似于 hex(X),bin(X),oct(X)
    __enter__,__exit__ 在对类对象执行类似 with obj as var 的操作之前,会先调用 __enter__ 方法,其结果会传给 var;在最终结束该操作之前,会调用 __exit__ 方法(常用于做一些清理、扫尾的工作)
    [root@kube method]# cat demo17.py    #其中 __str__ , __lt__ , __add__ 都是重载运算符   ,使用相对应的运算符就会调用这些方法
    class MyClass: #自定义一个类
        def __init__(self, name , age): #定义该类的初始化函数                     
            self.name = name #将传入的参数值赋值给成员交量
            self.age = age
        def __str__(self): #用于将值转化为字符串形式,等同于 str(obj)
            return "name:"+self.name+";age:"+str(self.age)
       
        __repr__ = __str__ #转化为供解释器读取的形式
       
        def __lt__(self, record): #重载 self<record 运算符
            if self.age < record.age:
                return True
            else:
                return False
       
        def __add__(self, record): #重载 + 号运算符
            return MyClass(self.name, self.age+record.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) #比较 myc<mycl 的结果,输出 False
    print (myc+mycl) #进行两个 MyClass 对象的相加运算,输出 "name:Anna;age:65"
    [root@kube method]# py demo17.py 
    name:Anna;age:42
    name:Anna;age:42
    name:Anna;age:42
    False
    name:Anna;age:65
    [root@kube method]# 

    Python重载运算符实现自定义序列

     Python 可重载的运算符中,有如下几个和序列操作相关的特殊方法:

    • __len__(self):该方法的返回值决定序列中元素的个数。
    • __getitem__(self, key):该方法获取指定索引对应的元素。该方法的 key 应该是整数值或 slice 对象,否则该方法会引发 KeyError 异常。
    • __contains__(self, item):该方法判断序列是否包含指定元素。
    • __setitem__(self, key, value):该方法设置指定索引对应的元素。该方法的 key 应该是整数值或 slice 对象,否则该方法会引发 KeyError 异常。
    • __delitem__(self, key):该方法删除指定索引对应的元素。


    在此基础上,如果根据特定的需求对这些方法进行重载,即可实现自定义一个序列类。

    什么是迭代器,Python迭代器及其用法

     前面介绍了使用 for 循环遍历列表、元组和字典等,这些对象都是可迭代的,因此它们都属于迭代器。迭代器其实就是一个实现了迭代器协议的容器类对象。

    简单的理解容器,就是用来存放各种元素的,是各种元素的集合。常用的容器包括元组、列表、字典、集合等。

    迭代器基于以下两个方法实现:

    1. __next__(self):返回容器的下一个元素。
    2. __iter__(self):该方法返回一个迭代器(iterator)。


    迭代器可以利用 Python 内置的 iter() 函数和一个序列来创建。例如:

    [root@kube method]# cat demo18.py 
    #coding:utf-8
    
    my_iter = iter([1,'abc','jojo'])
    print(my_iter.__next__())
    print(my_iter.__next__())
    print(my_iter.__next__())
    print(my_iter.__next__())
    [root@kube method]# py demo18.py 
    1
    abc
    jojo
    Traceback (most recent call last):
      File "demo18.py", line 7, in <module>
        print(my_iter.__next__())
    StopIteration
    [root@kube method]# 

    可以看到,当遍历完序列之后如果继续遍历,会引发 StopIteration 异常,这样迭代器就可以与循环兼容,因为可以捕获这个异常并停止循环。例如:

    [root@kube method]# cat demo18.py 
    #coding:utf-8
    
    my_iter = iter([1,'abc','jojo'])
    for i in my_iter:
        print(i)
    [root@kube method]# py demo18.py 
    1
    abc
    jojo
    [root@kube method]# 

    Python 也允许我们自定义一个迭代器,只需要实现 __next__() 和 __iter__() 特殊方法即可。例如:

    [root@kube method]# cat demo19.py 
    #coding:utf-8
    
    class countDown:
        def __init__(self,step):
            self.step = step
        def __iter__(self):    #将类转换为迭代器
            return self
        def __next__(self):     #使用 __next__ 进行迭代
            if self.step <= 0:
                raise StopIteration
            self.step -= 1 
            return self.step
    
    for i in countDown(9):
        print(i, end= ' ')
    [root@kube method]# py demo19.py 
    8 7 6 5 4 3 2 1 0 [root@kube method]# 

    Python项目实战之迭代器实现字符串的逆序输出

    [root@kube method]# cat demo20.py 
    #coding:utf-8
    
    class Reverse:
        def __init__(self, string):
            self.__string = string
            self.__index = len(string)
        def __iter__(self):
            return self
        def __next__(self):
            if self.__index == 0:
                raise(StopIteration)
            self.__index -= 1
        
            return self.__string[self.__index]
    
    for i in Reverse('Python'):
        print(i , end= ' ')
    [root@kube method]# py demo20.py 
    n o h t y P [root@kube method]# 

    Python生成器详解

    生成器和迭代器的功能非常相似,它也会提供 __next__() 方法,这意味着程序同样可调用内置的 next() 函数来获取生成器的下一个值,也可使用 for 循环来遍历生成器。

    生成器与迭代器的区别在于,迭代器通常是先定义一个迭代器类,然后通过创建实例来创建迭代器;而生成器则是先定义一个包含 yield 语句的函数,然后通过调用该函数来创建生成器。

    生成器是一种非常优秀的语法,Python 使用生成器可以让程序变得很优雅。

    创建生成器

    创建生成器需要两步操作:

    1. 定义一个包含 yield 语句的函数。
    2. 调用第 1 步创建的函数得到生成器。
    [root@kube method]# cat demo21.py 
    #coding:utf-8
    
    def test(val):
        for i in range(val):
            yield i      #创建一个包含 yield 语句的函数
    
    t = test(10)
    print(t)
    print(next(t))    #使用next() 方法调用 函数 ,每次执行只得到一个返回结果
    for i in t:
        print(i ,end=' ')
    [root@kube method]# py demo21.py 
    <generator object test at 0x7fac02d3e9d0>
    0
    1 2 3 4 5 6 7 8 9 [root@kube method]# 

     系统学习本教程的读者应该知道,前面章节还介绍过使用 for 循环来创建生成器(将 for 表达式放在圆括号里)。可见,Python 主要提供了以下两种方式来创建生成器:

    1. 使用 for 循环的生成器推导式。
    2. 调用带 yield 语句的生成器函数。


    生成器是 Python 的一个特色功能,在其他语言中往往没有对应的机制,因此很多 Python 开发者对生成器机制不甚了解。但实际上生成器是一种非常优秀的机制,以我们实际开发的经验来看,使用生成器至少有以下几个优势:

      • 当使用生成器来生成多个数据时,程序是按需获取数据的,它不会一开始就把所有数据都生成出来,而是每次调用 next() 获取下一个数据时,生成器才会执行一次,因此可以减少代码的执行次数。比如前面介绍的示例,程序不会一开始就把生成器函数中的循环都执行完成,而是每次调用 next() 时才执行一次循环体。
      • 当函数需要返回多个数据时,如果不使用生成器,程序就需要使用列表或元组来收集函数返回的多个值,当函数要返回的数据量较大时,这些列表、元组会带来一定的内存开销。如果使用生成器就不存在这个问题,生成器可以按需、逐个返回数据。
      • 使用生成器的代码更加简洁。

    Python @函数装饰器及用法(超级详细)

     前面章节中,我们已经讲解了 Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod、classmethod 和 property 都是 Python 的内置函数。那么,我们是否可以开发自定义的函数装饰器呢?

    答案是肯定的。当程序使用“@函数”(比如函数 A)装饰另一个函数(比如函数 B)时,实际上完成如下两步:

    1. 将被修饰的函数(函数 B)作为参数传给 @ 符号引用的函数(函数 A)。
    2. 将函数 B 替换(装饰)成第 1 步的返回值。


    从上面介绍不难看出,被“@函数”修饰的函数不再是原来的函数,而是被替换成一个新的东西(取决于装饰器的返回值)。其实所谓的装饰器,就是通过装饰器函数,来修改原函数的一些功能,使得原函数不需要修改。

    [root@kube method]# cat demo24.py 
    #coding:utf-8
    
    def A(fn):
        print('a_fun')
        fn()         #执行传入的函数fn
        print('a_END')
    @A           #对函数B()进行修饰
    def B():
        print('B_fun')
    
    print(B)
    
    [root@kube method]# py demo24.py 
    a_fun
    B_fun
    a_END
    None
    [root@kube method]#

     别忘记了,被修饰的函数总是被替换成 @ 符号所引用的函数的返回值,因此被修饰的函数会变成什么,完全由于 @ 符号所引用的函数的返回值决定,换句话说,如果 @ 符号所引用的函数的返回值是函数,那么被修饰的函数在替换之后还是函数。

    带参数的函数装饰器

    [root@kube method]# cat demo24.py 
    #coding:utf-8
    
    def A(fn):
        print('a_fun')
        fn()   #执行传入的函数fn
        print('a_END')
    @A
    def B():
        print('B_fun')
    
    print(B)
    
    [root@kube method]# py demo25.py 
    <function foo.<locals>.bar at 0x7f86d0705440>
    ==my_test函数== 90
    ***************
    ==my_test函数== 90
    [root@kube method]# 

    上面程序定义了一个装饰器函数 foo,该函数执行完成后并不是返回普通值,而是返回 bar 函数(这是关键),这意味着被该 @foo 修饰的 my_test() 函数最终都会被替换成 bar() 函数。

    上面程序使用 @foo 修饰 my_test() 函数,因此程序同样会执行 foo(my_test),并将 my_test 替换成 foo() 函数的返回值:bar 函数。所以,上面程序第 18 行代码在打印 my_test 函数时,实际上输出的是 bar 函数,这说明 my_test 已经被替换成 bar 函数。接下来程序调用 my_test() 函数,实际上就是调用 bar() 函数。

    接收任意数量和类型的参数

    [root@kube method]# cat demo26.py 
    def foo(fn):
        # 定义一个嵌套函数
        def bar(*args,**kwargs):
            fn(*args,**kwargs)
        return bar
    @foo
    def my_test(a):
        print("==my_test函数==", a)
    @foo
    def new_test(a,b):
        print("==new_test函数==",a," ",b)
    my_test(10)
    new_test(6, 5)
    [root@kube method]# py demo26.py 
    ==my_test函数== 10
    ==new_test函数== 6   5
    [root@kube method]# 

    带自定义参数的函数装饰器

    ,装饰器可以接受原函数任意类型和数量的参数,除此之外,它还可以接受自己定义的参数。

    举个例子,比如要定义一个参数,来表示装饰器内部函数被执行的次数,那么就可以写成下面这种形式:

    [root@kube method]# cat demo27.py 
    def foo(num):
        def my_decorator(fn):
            def bar(*args,**kwargs):
                for i in range(num):
                    fn(*args,**kwargs)
            return bar
        return my_decorator
    @foo(3)
    def my_test(a):
        print("==my_test函数==", a)
    @foo(5)
    def new_test(a,b):
        print("==new_test函数==",a," ",b)
    my_test(10)
    new_test(6, 5)
    [root@kube method]# py demo27.py 
    ==my_test函数== 10
    ==my_test函数== 10
    ==my_test函数== 10
    ==new_test函数== 6   5
    ==new_test函数== 6   5
    ==new_test函数== 6   5
    ==new_test函数== 6   5
    ==new_test函数== 6   5
    [root@kube method]# 

    函数装饰器也可以嵌套

    上面示例中,都是使用一个装饰器的情况,但实际上,Python 也支持多个装饰器,比如:
    @decorator1
    @decorator2
    @decorator3
    def func():
        ...
    上面程序的执行顺序是里到外,所以它等效于下面这行代码:
    decorator1( decorator2( decorator3(func) ) )
  • 相关阅读:
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(9)--发布InfoPath表单
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(8)--修改InfoPath表单
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(7)--从初始表单抽取值
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(6)--创建初始和关联表单
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(5)--创建全局可重用工作流
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(4)--重用全局可重用工作流
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(3)--使用工作流模板
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(2)--创建关联栏目
    一步步学习SPD2010--第九章节--使用可重用工作流和工作流表单(1)--创建和使用可重用工作流
    深度学习模型调优方法(总结)
  • 原文地址:https://www.cnblogs.com/zy09/p/11686814.html
Copyright © 2011-2022 走看看