zoukankan      html  css  js  c++  java
  • python学习笔记25:基础语法之class

    1. 基本语法

    名词 解释
    类创建一个新类型;是一个抽象的模板;
    对象/实例 类的实例;每个对象拥有相同的方法,但数据可能不同;
    属于一个类或对象的变量,用于存储数据;有两种类型:实例变量、类变量;
    方法 属于一个类的函数;
    属性 域和方法合称为属性;
    实例变量 属于每个实例(类的对象)的域;
    类变量 属于类本身的域;
    class Dog(object): # 类,创建一个新类型,是一个抽象的模板
        LEG_CNT = 4    # 类变量,该类的所有实例共享
    
        def __init__(self, a): # 构造函数,在类实例化时自动调用, 定义需要一个额外的self参数。
            self.a = a         # 实例变量,每个实例单独赋值,不共享
    
        def func0(self):       # 实例方法,需要一个self参数,表示实例本身。
            print(f'Calling Func0, a = {self.a}')           # 可以访问实例变量
            print(f'Calling Func0, LEG_CNT={self.LEG_CNT}') # 可以访问类变量
    
        @classmethod     # 这是个装饰器,它装饰的方法会变成类方法
        def func1(cls):  # 类方法需要cls参数,为类本身
            print(f'Calling Func1, LEG_CNT={cls.LEG_CNT}')  # 可以访问类变量
            cls.LEG_CNT += 1                                # 可以修改类变量
            print(f'Calling Func1, LEG_CNT={cls.LEG_CNT}')
    
            #cls.func0() # 调用实例方法,直接调用会报错: 需要self参数, 所以需要加上实例参数才能调用
            #cls.a       # 不能访问实例变量,报告无此属性
    
        @staticmethod    # 这是个装饰器,它装饰的方法会变成静态方法
        def func2():     # 静态方法不需要额外的self/cls参数.
            # 静态方法不需要(也不能)访问类对象或类实例的其它成员和方法(因为没有cls或self参数),
            # 只是把函数嵌入到了类中,以方便继承或组织代码 
            print('Calling Static Func2')
    
    >>> d0 = Dog(0)
    >>> d0.func0()  # 通过实例名调用实例方法
    Calling Func0, a = 0
    Calling Func0, LEG_CNT=4
    >>> 
    >>> Dog.func1()   # 通过class名调用类方法
    Calling Func1, LEG_CNT=4
    Calling Func1, LEG_CNT=5
    
    >>> Dog.func0(d0) # 通过class名,传入实例,调用实例方法
    Calling Func0, a = 0
    Calling Func0, LEG_CNT=5
    >>> 
    >>> Dog.func2() # 通过class名调用静态方法
    Calling Static Func2
    >>> d0.func2()  # 通过实例名调用静态方法
    Calling Static Func2
    

    变量访问

    命名方法 举例 访问限制
    双下划线开头 __name 私有变量,外部不能访问。
    双下划线开头、结尾 __name__ 特殊变量,外部可以访问。
    单下划线开头 _name 私有变量,但外部可以访问。

    2. 多重继承

    多重继承时,super()的执行顺序
    例1:
    class继承关系

    graph LR A[class C20 <br/> from C10 C11] -- 继承 --> B[class C10 <br/> from C00] A --> C[class C11 <br/> from C00] B --> D[class C00] C --> D
    class C00():
        def run(self):
            print('run C00')
            
    class C10(C00):
        def run(self):
        	super(C10, self).run()
            print('run C10')        
                    
    class C11(C00):
        def run(self):
        	super(C11, self).run()
            print('run C11')
            
    class C20(C10, C11):
        def run(self):
        	super(C20, self).run()
            print('run C20')        
                    
    

    执行:

    >>> C20.__mro__  
    (<class ‘__main__.C20’>, <class ‘__main__.C10’>, <class ‘__main__.C11’>, <class ‘__main__.C00’>, <class ‘object’>)  
    >>> c = C20()  
    >>> c.run()  
    run C00  
    run C11  
    run C10  
    run C20  
    

    解释:
    C20.__mro__是一个元组,采用广度优先原则(同一层级的优先,不同于深度优先),每个super函数调用的都是C20.__mro__中下一个元素对应类的函数;

    >>> C20.__mro__  
    (  
        <class '__main__.C20'>,  
        <class '__main__.C10'>,  
        <class '__main__.C11'>,  
        <class '__main__.C00'>,  
        <class 'object'>,  
    )  
    
                             /----------------       /----------------       /----------------       /----------------          
                             |                |       |C20.__mro__[1]: |       |C20.__mro__[2]: |       |C20.__mro__[3]: |          
                           ->|                |     ->|cls __main__.C10|     ->|cls __main__.C11|     ->|cls __main__.C00|          
                          /  |C20.run()       |    /  |C10.run()       |    /  |C11.run()       |    /  |C00.run()       |          
                        /    ----------------/  /    ----------------/  /    ----------------/  /    ----------------/          
                      /              |         /              |         /              |         /              |                   
    /----------------       /----------------       /----------------       /----------------               |                   
    |    c = C20()   |       |super(C20, self)|       |super(C10, self)|       |super(C11, self)|               |                   
    |    c.run()     |       |    .run()      |       |    .run()      |       |    .run()      |               |                   
    |      (1)       |       |      (2)       |       |      (3)       |       |      (4)       |               |                   
    ----------------/       ----------------/       ----------------/       ----------------/               |           #output 
                                     |                        |                        |             (5) print('run C11')--"run C00"
                                     |                        |             (6) print('run C11')---------------------------"run C11"
                                     |             (7) print('run C10')----------------------------------------------------"run C10"
                          (8) print('run C20')---------------------------------------------------------------------------- "run C20"
    
    
    1. c.run(), 调用C20.run()
    2. C20.run()第一行,super(C20, self).run(),mro[1],调用C10.run()
    3. C10.run()第一行,super(C10, self).run(),mro[2],调用C11.run()
    4. C11.run()第一行,super(C11, self).run(),mro[3],调用C00.run()
    5. C00.run()没有super,打印’run C00’, C00.run()执行完毕
    6. C11.run()第二行,打印‘run C11’,C11.run()执行完毕
    7. C10.run()第二行,打印‘run C10’,C10.run()执行完毕
    8. C20.run()第二行,打印‘run C20’,C20.run()执行完毕

    3. 魔术方法

    3.1. __str__()

    str:改变 print(类实例)时显示的内容。

    普通class

    >>> class CTest0():  
    ...     pass  
    ...  
    >>> t0 = CTest0()  
    >>> t0  
    <__main__.CTest0 object at 0x2b***>  
    >>> print(t0)  
    <__main__.CTest0 object at 0x2b***>  
    

    重构__str__()

    >>> class CTest1():  
    ...     def __str__(self):  
    ...         return ‘Class {} obj’.format(self.__class__.__name__)  
    ...  
    >>> t1 = CTest1()  
    >>> t1 # 直接输出对象,与默认情况相同  
    <__main__.CTest1 object at 0x2b***>  
    >>> print(t1) # 打印类实例,输出__str__()方法的返回值 
    <Class CTest1 obj>  
    

    3.2. __repr__()

    repr:改变直接输出对象和 print(类实例) 时显示的内容
    重构__repr__()

    >>> class CTest2():  
    ...     def __repr__(self):  
    ...         return ‘Class {} obj’.format(self.__class__.__name__)  
    ...  
    >>> t2 = CTest2()  
    >>> t2 # 直接输出对象,输出__repr__()方法的返回值  
    <Class Ctest2 obj>  
    >>> print(t2) # 打印类实例,输出__repr__()方法的返回值  
    <Class CTest2 obj>  
    

    3.3. __iter__()

    iter()方法:返回一个迭代对象;
    next()方法:迭代__iter__()返回的对象时,会调用__next__()方法拿到循环的下一个值,直到遇到StopIteration错误;

    class Fib():  
        def __init__(self):  
            self.a, self.b = 0, 1  
      
        def __iter__(self):  
            return self  
      
        def __next__(self):  
            self.a, self.b = self.b, self.a+self.b  
            if self.a > 100:  
                raise StopIteration()  
            return self.a  
      
    >>> #对class 实例进行循环  
    >>> for i in Fib():  
    ...     print(i, end=‘ ’)  
    ...  
    1 1 2 3 5 8 13 21 34 55 89  
    

    3.4. __getitem__()

    使用__iter__()方法虽然可以对类实例做循环,但不能用下标取元素,
    使用__getitem__()方法可以实现“用下标取元素“;

    class Fib():  
        def __getitem__(self, n):  
            a, b = 1, 1 # 如果n==0,则不进入for循环,直接返回1,保证[0]==1  
            for i in range(n):  
                a, b = b, a+b  
       
        return a  
      
    >>> #对类实例进行取下标  
    >>> Fib()[8]  
    34  
    >>> f = Fib()  
    >>> f[8]  
    34  
    

    使用__getitem__()可实现“按下标取元素“,但不能处理切片、不能“按下标赋值”、不能删除元素,这些都可以通过添加相应的方法来完成。

    拦截对[‘’]方式的属性调用

    3.5. __setitem__()

    class A:  
      def __init__(self, cfg={}):  
        self.cfg = cfg  
      
      def __setitem__(self, k, v):  
        self.cfg[k] = v  
      
      def __getitem__(self, k):  
        return self.cfg[k]  
    

    3.6. __getattr__()

    当调用不存在的属性时,正常情况下会报告AttributeError: ‘XX’ object has no attribute ‘YY’;
    可以通过__getattr__()方法动态返回一个属性,这时如果调用不存在的属性时,Python就会调用__getattr__(self, ‘attr’)来获取属性.

    class Student():  
        def __init__(self):  
            self.name = ‘Jim’  
       
        def __getattr__(self, attr):  
            if attr == ‘score’:  
                return 99  
            raise AttributeError(‘’Student’ object has no attribute xx’)  
      
    >>> # 尝试调用属性score:  
    >>> s = Student()  
    >>> s.name  
    ‘Jim’  
    >>> s.score # 本没有score这个属性,但通过__getattr__()方法获取了返回值  
    99  
    

    拦截属性取值语句, 即 a = obj.xx

    3.7. __setattr__()

    拦截属性的赋值语句, 即 obj.xx = yy

    class F(object):
    def setattr(self, key, value):
    self.dict[key] = value

    3.8. __call__()

    使用__call__()方法可以使类实例变得“可调用”,类实例就象一个函数一样;

    class Student():  
        def __init__(self, name):  
            self.name = name  
       
        def __call__(self):  
            print(f’My name is {self.name}’)  
      
    >>> #尝试调用类实例  
    >>> s = Student(‘Jim’)  
    >>> s() # 此句会调用类的__call__()方法  
    My name is Jim  
    

    3.9. __new__()

    new()与__init__()的区别:

    1. new()方法先调用, init()方法后调用.
    2. new()是class级别的方法, 用于控制一个新instance的生成过程.
    3. new()需要一个参数cls, (但又不需要声明它是@classmethod).
    4. new()必须返回实例化出来的实例.
    5. init()是instance级别的方法, 用于初始化一个新实例.
    class Person(object):  
        def __new__(cls, *args, **kwargs):  
            print(f'__new__() called. {args} {kwargs}')  
            return super(Person, cls).__new__(cls) #NOTE, there is no args  
      
        def __init__(self, name, age):  
            print('__init__() called.')  
            self.name = name  
            self.age  = age  
      
        def __str__(self):  
            return f'<Person: {self.name}({self.age})>'  
      
    p = Person('n0', age=24)  
    print(p)  
    

    执行结果如下:

    __new__() called. ('n0',) {'age': 24} # new方法先执行  
    __init__() called.                    # init方法后执行  
    <Person: n0(24)>  
    

    对于p = Person(name, age),

    1. 首先使用参数name和age来执行Person.new(name, age), new()会返回Person类的一个实例, 通常是使用这种方式: super().new(cls, …).
    2. 然后使用__new__()返回的实例调用__init__()方法.

    new()方法的使用场景:

    1. 继承不可变的class(比如str, int, tuple等)时. 如永远是正数的整型.
    class PosInt(int):  
        def __init__(self, i):  
            super().__init__() #NOTE: here takes no parameters.  
      
    i0 = PosInt(-3)  
    print(i0) # -3  
      
    class PosInt(int):  
        def __new__(cls, i):  
            return super().__new__(cls, abs(i))  
      
    i0 = PosInt(-3)  
    print(i0) # 3  
    
    1. 实现单例模式, 通过__new__()方法返回实例.

    3.10. __eq__()

    重新定义类的==行为.

    class CTest():  
        def __init__(self, i_value):  
            self.i_value = i_value  
      
        def __eq__(self, other):  
            #当两个实例的value相差<4时, 认为相等  
            if abs(self.i_value-other.i_value)<4:  
                return True # 返回True表示相等  
            else:  
                return False # 返回False表示不等  
      
    if __name__ == '__main__':  
        t1 = CTest(1)  
        t2 = CTest(2)  
        t7 = CTest(7)  
      
        print(t1 == t2) # True  
        print(t1 == t7) # False  
    

    4. 控制class的创建

    4.1. type()

    作用:可以动态创建类。

    以下代码定义了一个类:

    class Hello(object):  
        def hello(self, name=‘world’):  
            print(‘Hello, %s’%(name))  
    

    等价于以下代码,使用type(),Python解释器遇到class定义时,就是调用type()创建class的。

    def fn(self, name=‘world’):  
        print(‘Hello, %s’%(name))
    
    Hello=type(‘Hello’, (object,), dict(hello=fn)) #创建Hello class  
    

    创建class对象时,type的3个参数:

    1. class名称;
    2. 继承的父类集合(如果只有一个父类,tuple单元素需要一个逗号);
    3. class的method名称与函数绑定(这里把函数fn绑定到method hello上);

    对于上面两种class定义的方式,以下测试结果相同

    >>> h = Hello()  
    >>> h.hello()  
    Hello, world  
    >>> print(type(Hello)) # Hello是通过type创建的,所以它的type是type。  
    <class ‘type’>  
    >>> print(type(h)) # h是通过Hello创建的,所以它的type是Hello。  
    <class ‘__main__.Hello’>  
    

    4.2. metaclass

    metaclass可以控制类的创建行为(创建类或修改类)
    metaclass、类、实例,三者的关系:根据metaclass创建类、根据类创建实例;

    定义metaclass:metaclass是类的模板,所以从type类派生;

    class ListMetaclass(type): # metaclass的类名总以Metaclass结尾,以方便识别  
        def __new__(cls, name, bases, attrs):  
            attrs[‘add’] = lambda self, value: self.append(value)  
            return __new__(cls, name, bases, attrs)  
    

    new()方法的参数:

    1. 待创建的类对象;
    2. 类名字;
    3. 类的父类集合;
    4. 类的方法集合;

    利用metaclass来定制类(指导类的创建方式)

    class MyList(list, metaclass=ListMetaclass):  
        pass 
    

    使用metaclass参数,Python创建MyList时,会通过ListMetaclass.new()来创建,所以MyList这个类多了一个方法:add()

    测试:

    >>> L = MyList()  
    >>> L  
    []  
    >>> L.add(1) # L有add()方法  
    >>> L  
    [1]  
    >>> L2 = list()  
    >>> L2.add(1) # L2没有add()方法  
    AttributeError: ‘list’ object has no attribute ‘add’  
    

    在ORM(Object Relational Mapping,对象-关系映射,把关系数据库的行为映射为一个对象,即一个类对应一个表)框架中,类只能动态定义,因为类的定义取决于表的结构,只有使用者才能根据表的结构定义出对应的类。

  • 相关阅读:
    centos7安装kafka
    Qt——透明无边框Widget的bug
    Qt——浅谈样式表
    Qt——QLineEdit使用总结
    Qt——信号槽连接:基于字符串与基于函数的连接之间的不同
    Qt——树的搜索实现源码
    Qt——树结点的搜索
    Qt——鼠标拖动调整窗口大小
    Qt——右键菜单
    Qt——正则表达式
  • 原文地址:https://www.cnblogs.com/gaiqingfeng/p/13255480.html
Copyright © 2011-2022 走看看