zoukankan      html  css  js  c++  java
  • guxh的python笔记五:面向对象

    1,面向对象编程思想

    类:一类具有相同属性的抽象

          属性(静态属性):实例变量、类变量、私有属性

          方法(动态属性):构造函数、析构函数(默认就有)、函数、私有函数

    对象/实例:类经过实例化后,就是对象/实例

    封装 encapsulation:隐藏对象的属性和实现细节,仅对外公开接口

    继承 inheritance:类派生子类

    多态 polymorphism:一个接口,多种实现

    举例:

    class Foo:  # 类
        t = 'd'  # 类变量,所有实例共享
    def __init__(self, x, y): # 构造函数 self.x = x # 属性,实例变量 self._y = y # 私有属性,实例变量 def _test(self): # 私有方法 return self.x def __del__(self): # 析构函数 pass def run(self): print(self.x, self._y) f = Foo(1, 2) # 类的实例化 f.run() # 调用实例f的方法run Foo.run(f) # 调用实例f的方法run

    类变量t,可以通过Foo.t,  type(self).t,  self.__class__.t, @classmethod的cls.t去访问和修改。

    如果用self.t虽然也可以访问,但修改类变量时,如果t是不可变参数会创建副本。如果是可变序列则用self.t访问和用类访问一样会就地修改,参考函数的传参。

    >>>f.t
    d
    >>>f.t = 'e'
    >>>f.t.  
    e
    >>>Foo.t
    d
    

    2,继承

    代码重用

    基类不应该被单独实例化

    2.1,继承的方法

    class Human:
        pass
    
    class Male(Human):
        pass
    

    2.2,子类继承父类的构造函数或方法

    不会自动获得父类构造函数或方法,直接写会覆盖父类的构造函数或者同名方法。

    如果想要获得父类的构造函数或者方法,需要通过继承获得。

    可以通过super()或指定父类继承构造函数或者方法:

    class Base:
        def __init__(self):
            print('BASE_init')
    
        def say(self):
            print('BASE_say')
    
    class A(Base): 
        def __init__(self): 
    	    super().__init__()  # 通过super()继承构造函数 
    
        def say(self):
            super().__init__()  # 通过supe r()继承方法
    
    class B(Base): 
        def __init__(self): 
            Base.__init__(self) # 通过指定父类继承构造函数
    
        def say(self):
            Base.__init__(self) # 通过指定父类继承方法
    

      

    2.3,指定父类多重继承

    指定父类多重继承构造函数或者方法,结果相同,本节以构造函数为例。

    指定父类多重继承:

    class Base:
        def __init__(self):
            print('BASE')
    
    class A(Base):
        def __init__(self):
            Base.__init__(self)
            print('A')
    
    class B(Base):
        def __init__(self):
            Base.__init__(self)
            print('B')
    
    class C(A, B):
        def __init__(self):
            A.__init__(self)
            B.__init__(self)
            print('C')

    上述代码中,AB都指定继承Base,C先后指定继承AB。

    对C实例化时,先后执行AB的构造函数,再执行C自己的构造函数,但Base的构造函数被执行了2遍。

    >>> c = C()
    BASE
    A
    BASE
    B
    C  

    2.4,super()多重继承

    super()多重继承构造函数或者方法,结果相同,本节以构造函数为例。

    每个重新定义过的覆盖方法都使用了super()方法时,继承时会遍历整个MRO列表,并且让每个方法只调用一次。(全有super:遍历)

    不符合上面条件时,继承时会按照MRO列表继承,继承时执行广度优先继承,同时不会遍历MRO列表,如果某个类有super()时,会继承该类后面的类的方法。(非全有super:广度优先 + MRO列表super)

    2.5实验中,C的MRO列表打印如下:

    >>> C.__mro__
    (<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>)

    1)super()多重继承 — A和B都使用了super()

    class Base:
        def __init__(self):
            print('BASE')
    
    class A(Base):
        def __init__(self):
            super().__init__()
            print('A')
    
    class B(Base):
        def __init__(self):
            super().__init__()
            print('B')
    
    class C(A, B):
        def __init__(self):
            super().__init__()
            print('C')

    C实例化:

    >>> c = C()
    BASE
    B
    A
    C
    

    Base,B,A,C都得到了遍历执行,并且Base的构造方法只执行了一遍。

    2)super()多重继承 — A和B都没有使用super()

    class Base:
        def __init__(self):
            print('BASE')
    
    class A(Base):
        def __init__(self):
            print('A')
    
    class B(Base):
        def __init__(self):
            print('B')
    
    class C(A, B):
        def __init__(self):
            super().__init__()
            print('C')

    C实例化:

    >>> c = C()
    A
    C

    根据MRO列表优先只继承A。

    如果A没有构造方法,根据广度优先继承B,输出结果是B,C。

    3)super()多重继承 — B有super()

    C实例化时,输出结果为AC。同2)super()多重继承 — A和B都没有使用super()

    如果A没有构造方法,根据广度优先继承B,输出结果是BASE,B,C。(多个BASE是因为B有super())

    4)super()多重继承 — A有super()

    class Base:
        def __init__(self):
            print('BASE')
    
    class A(Base):
        def __init__(self):
            super().__init__()
            print('A')
    
    
    class B(Base):
        def __init__(self):
            print('B')
    
    class C(A, B):
        def __init__(self):
            super().__init__()
            print('C')

    奇怪的现象出现了:

    >>> c = C()
    B
    A
    C

    这是因为根据MRO列表,顺序为C/A/B/BASE,A的super()继承的是B的构造函数,并不是BASE的构造函数。 

     

    2.5,使用mixin类技术扩展类的功能

    mixin类时为了扩展其他类的功能而创建的,不能直接实例化,没有__init__(),也没有实例变量

    mixin生效的关键是通过super()把任务转交给MRO中的下一个类。

    MRO解析顺序为从左至右(object),所以需要注意继承的次序,不确定的话可以通过print(cls.mro())查看。

    mixin类实例一:记录字典赋值过程

    class LoggedMappingMixin:
    
        __slots__ = ()
        
        def __getitem__(self, item):
            print('Getting ' + str(item))
            return super().__getitem__(item)
    
        def __setitem__(self, key, value):
            print('Setting {} = {}'.format(key, value))
            return super().__setitem__(key, value)
    
        def __delitem__(self, key):
            print('Deleting ' + str(key))
            return super().__delitem__(key)

    使用mixin类:

    class LoggedDict(LoggedMappingMixin, dict):  # 需要先继承mixin类
        pass
    
    >>>LoggedDict.mro() # mixin的super继承的是dict
    [<class '__main__.LoggedDict'>, <class '__main__.LoggedMappingMixin'>, <class 'dict'>, <class 'object'>]
    >>>d['x'] = 42
    Setting x = 42
    >>>d['x']
    Getting x
    42
    

    mixin类实例二:实现key类型限制

    class RestrictKeysMixin:
        def __init__(self, *args, _restrict_key_type, **kwargs):
            self._restrict_key_type = _restrict_key_type
            super().__init__(*args, **kwargs)
    
        def __setitem__(self, key, value):
            if not isinstance(key, self._restrict_key_type):
                raise TypeError('Keys must be ' + str(self._restrict_key_type))
            super().__setitem__(key, value)
    
    class Rdict(RestrictKeysMixin, dict):
        pass
    
    d = Rdict(_restrict_key_type=str)
    d[42] = 10  # TypeError: Keys must be <class 'str'>
    

      

    3,多态

    3.1,实现多态

    在父类中的一个方法,输入不同子类的对象时,可以实现不同的效果。

    父类:

    class Human:
        
        def __init__(self, name, age):
            self.name = name
            self.age = age
    

    子类Male:

    class Male(Human):
    
        def hobby(self):
            print('sports')
    

    子类Female:

    class Female(Human):
    
        def hobby(self):
            print('shopping')
    

    不同子类,hobby输出不同,实现了多态:

    m = Male('m', 20)
    f = Female('f', 20)
    m.hobby()  # sports
    f.hobby()  # shopping
    

    还可以编写一个入口函数,统一输出不同对象的hobby:

    def get_hobby(obj):
        obj.hobby()
    
    get_hobby(m)  # sports
    get_hobby(f)  # shopping
    

    3.2,通过抽象基类强制要求子类必须实现方法

    如果想要求子类在继承时必须实现hobby方法,可以对父类进行如下改造:

    import abc
    
    class Human(abc.ABC):
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
        
        @abc.abstractmethod 
        def hobby(self):    # 抽象方法无需实现,直接注释或pass即可
            """必须实现hobby"""
    

    4,内置三大类装饰器

    4.1,@staticmethod

    如果一个方法在类中定义,但没有用到类中的实例属性或者方法,建议加上staticmethod或者classmethod。

    staticmethod:本质上就是放在类中的全局函数(可以通过类或实例调用),和类没关系,完全可以放在类的外面。

    class Foo:
        def __init__(self, x):
            self.x = x
    
        @staticmethod
        def run():
            print('hello world')
    
    f = Foo(1)
    f.run()     # 输出hello world,通过类的实例调用
    Foo.run()   # 输出hello world,通过类调用
    

     4.2,@classmethod

    被@classmethod装饰的函数可以通过类或者类的实例调用。

    和staticmethod的区别是可以调用类的属性或方法:

    class Foo:
        hw = 'hello world'
    
        @classmethod
        def klass_run(cls):
            print(cls.hw)
    
    f = Foo()
    f.klass_run()     # 输出hello world,通过类的实例调用
    Foo.klass_run()   # 输出hello world,通过类调用
    

    被classmethod装饰的函数第一个参数是类的引用,相当于类的实例self执行type(self),或者self.__class__,或者直接调用类Foo。下面实现了相同的效果:

    class Foo:
        hw = 'hello world'
    
        def run(self):   # 下面三个print实现了相同的效果
            print(type(self), type(self).hw)      
            print(self.__class__, self.__class__.hw)
            print(Foo, Foo.hw)
    
        @classmethod
        def klass_run(cls):
            print(cls, cls.hw)
    
    >>>f = Foo()
    >>>f.run()
    <class '__main__.Foo'> hello world
    <class '__main__.Foo'> hello world
    <class '__main__.Foo'> hello world
    >>>Foo.klass_run()
    <class '__main__.Foo'> hello world
    >>>f.klass_run()
    <class '__main__.Foo'> hello world
    

      

    4.3,@property

    主要三个功能,详见笔记‘类的属性’:

    • 函数变属性
    • 属性只读
    • 属性管理

    5,反射

    有时建立完类以后,需要动态操纵属性或方法。

    可以对类,或者类的实例进行操作

    class Foo:
        def __init__(self, x):
            self.x = x
    
        def run(self):
            print('hello world')
    
    f = Foo(1)
    

     hasattr:测试类/实例是否有属性/方法

    print(hasattr(Foo, 'x'))      # False
    print(hasattr(f, 'x'))        # True
    print(hasattr(Foo, 'run'))    # True
    print(hasattr(f, 'run'))      # True
    

    getattr:获取类/实例的属性/方法,如果某个方法保存在字符串中,可以用getattr获取并运行,可以实现相同功能的还有operator.methodcaller()

    getattr(f, 'run')()      # hello world
    print(getattr(f, 'x'))   # 1
    

    setattr:设置类/实例的属性/方法

    def fun(self):
        print('hello shanghai')
    
    setattr(Foo, 'fun', fun)       # 给Foo类增加fun方法
    f.fun()  
    setattr(f, 'fun', fun)        # 给f实例增加fun方法,调用时要把实例传进去
    f.fun(f)
    setattr(Foo, 'typecode', 'd')  # 设置类属性
    print(Foo.typecode)
    setattr(f, 'name', 'guxh')     # 设置类属性
    print(f.name)
    

    delattr:删除类/实例的属性/方法

    delattr(f, 'x')
    print(f.x)      # AttributeError
    

    6,构造函数

    6.1,简化构造函数

    有点类似namedtuple:

    class Structure:
        _fields = []
    
        def __init__(self, *args):
            if len(args) != len(self._fields):
                raise TypeError('Excepted {} arguments'.format(len(self._fields)))
    
            for name, value in zip(self._fields, args):
                setattr(self, name, value)
    
    class Stock(Structure):
        _fields = ['name', 'shares', 'price']
    
    s = Stock('IBM', 50, 68.6)
    print(s.name)    # ‘IBM'
    print(s.shares)  # 50
    print(s.price)   # 68.6
    

    注意,这里用的是setattr设置属性的键值对,如果有实例字典__dict__无法适应__slots__(元组保存)和描述符(或property)场景。

    6.2,使用@classmethod创建备选构造函数

    import time
    
    class Date:
    
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    
        @classmethod
        def today(cls):
            t = time.localtime()
            return cls(t.tm_year, t.tm_mon, t.tm_mday)
    
        def __repr__(self):
            return 'Date({}, {}, {})'.format(self.year, self.month, self.day)
    
    
    a = Date(2019, 10, 1)   # 实例a:使用构造函数
    b = Date.today()   # 实例b:使用备选构造函数
    

     在__init__中使用if...else...虽然也能创造缺省的实例,列入b=Date()时返回today,但是可读性不行

     

  • 相关阅读:
    C语言读写伯克利DB 4
    程序之美(转自知乎)
    C语言读写伯克利DB 3
    ON DUPLICATE KEY UPDATE
    nanomsg:ZeroMQ作者用C语言新写的消息队列库
    新浪研发中心: Berkeley DB 使用经验总结
    [企业开源系列]后起之秀Facebook凭什么挑战互联网霸主Google?
    BZOJ1770:[USACO]lights 燈(高斯消元,DFS)
    BZOJ5293:[BJOI2018]求和(LCA,差分)
    BZOJ5301:[CQOI2018]异或序列(莫队)
  • 原文地址:https://www.cnblogs.com/guxh/p/10262621.html
Copyright © 2011-2022 走看看