zoukankan      html  css  js  c++  java
  • 第三章:Python高级编程-深入类和对象

    第三章:Python高级编程-深入类和对象

    Python3高级核心技术97讲 笔记

    3.1 鸭子类型和多态

    """
    当看到一直鸟走起来像鸭子、游泳起来像鸭子、叫起来像鸭子,那么这只鸟就可以被称为鸭子。
    这句话看上去有趣,却不太容易理解。接下来用实例来说明。
    """
    
    
    # ============ Demo1 start =============
    class Cat(object):
        def say(self):
            print("I am a cat")
            
            
    class Dog(object):
        def say(self):
            print("I am a dog")
    
            
    class Duck(object):
        def say(self):
            print("I am a duck")
            
            
    animal = Cat
    animal().say()
    # ============ Demo1 end ===============
    
    
    # ============ Java pseudocode contrast start =============
    """
    在 Java中实现多态,需要子类继承父类并重写父类方法。并需要声明类型
    """
    
    class Animal:
        def say(self):
            print("I am an animal")
            
            
    class Dog(Animal):
        def say(self):
            print("I am an Doy")
            
            
    Ainmal animal = Cat()
    animal.say()
    # ============ Java pseudocode contrast end =============
    
    """
    在Python中就不一样了,如Demo1所示,变量animal可以指向任意类型,
    所有类不需要继承父类,只需定义相同的方法say()就可以实现多态。再调用的时候,
    只需调用共同say()方法。如下示例。
    """
    
    
    # ============== Demo2 start ===================
    class Cat(object):
        def say(self):
            print("I am a cat")
            
            
    class Dog(object):
        def say(self):
            print("I am a dog")
    
            
    class Duck(object):
        def say(self):
            print("I am a duck")
            
            
    animal_list = [Cat, Dog, Duck]
    for animal in animal_list:
        animal().say()  
    # ============== Demo2 end ===================
    
    """
    有内感觉了吗,反正我看到这,感触较深。嘎嘎嘎.....
    老师又来了个例子。
    """
    
    
    # ============== Demo3 start ====================
    a = ["bobby1", "bobby2"]
    name_tuple = ("bobby3", "bobby4")
    name_set = set()
    name_set.add("bobby5")
    name_set.add("bobby6")
    name_list = ["bobby7", "bobby8"]
    a.extend(name_tuple)
    a.extent(name_list)
    a.extent(name_set)
    # =============== Demo4 end ======================
    
    
    """
    在 Demo3 中不知你是否发现除了列表本身,元组和集合对象都可以传入列表对象的
    extend()方法。其实是extend()是接收一个可迭代对象,也就是前面章节所提到的
    迭代类型,那么好玩的就来了。
    """
    
    
    # =============== Demo5 start =====================
    class Dog(object):
        def say(self):
            print("I am a dog")
            
        def __getitem__(self):
            print("loop!!!!!!!!")
    
    a = ["bobby1", "bobby2"]
    dog = Dog()
    # name_tuple = ("bobby3", "bobby4")
    # name_set = set()
    # name_set.add("bobby5")
    # name_set.add("bobby6")
    # name_list = ["bobby7", "bobby8"]
    # a.extend(name_tuple)
    # a.extend(name_list)
    a.extend(dog)
    
    """
    结果:
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    loop!!!!!!!!
    ....
    """
    # =============== Demo5 end =======================
    
    
    """
    在 Demo5 中程序陷入了死循环,传入一个Dog对象也没有报错,
    为什么?因为魔法函数,前面章节提到的__getitem__()是的对象
    变成了可迭代对象,因此传入extend中,方法一直运行,知道抛出异常,
    但是示例中是不会抛出异常的,因此会陷入死循环。
    """
    

    3.2 抽象基类(abc模块)

    """
    abc -> abstract base class
    抽象基类相当于Java中的接口,Java无法实现多继承,
    但可以继承多个接口,接口是不可以实例化的。所以说,
    Python中的抽象基类也是不可以实例化的。Python是
    动态语言,是没有变量类型的。实际上,变量只是一个符
    号而已,它是可以指向任意类型的对象。动态语言不需要
    指定类型,所以就少了一个编译时检查错误的环境,只有运
    行时才知道错误。
    与Java最大的一个区别就是,在定义一个类的时候,是不
    需要去继承一个指定类型的。而要知道Python的一个类是
    属于哪个类型的,是去看实现了那些魔法函数,魔法函数赋予
    了类的一些特性。在实现了某个魔法函数之后,使得对象变成了
    一个指定的类型,这种方法,在Python中可以说是一种协议。
    在写代码是要尽量遵守这种协议,这样写出来的代码,才是
    足够Python的一种代码。
    """
    
    
    # ============ Demo1 start =============
    class Company(object):
        def __init__(self, employee_list):
            self.employee = employee_list
            
    	def __len__(self):
            return len(self.employee_list)
        
        
    com = Company(["bob", "jane"])
    # 如何判断对象的类型呢?
    # 第一种方案
    print(hasattr(com, '__len__'))  # 通过判断是否有某个属性而判断属于什么类型,不够直观
    
    # 通过抽象基类
    from collections.abc import Sized
    print(isinstance(com, Sized))  # 这样的方式更加直观,易读
    # ============ Demo2 end =============
    
    
    
    """
    抽象基类的两个使用场景:
    1. 我们在某些情况下希望判定某个对象的类型
    2. 我们需要强制某个子类必须实现某些方法
    """
    
    
    # =============== Demo2 start =================
    # 如何去模拟一个抽象基类
    class CacheBase():
        def get(self, key):
            raise NotImplementedError
            
    	def set(self, key, value):
            raise NotImplementedError
            
            
    class RedisCache(CacheBase):
        pass
    
    
    redis_cahe = RedisCache()
    redis_cache.set("key", "value")  # 会抛出异常,因为子类没有实现父类对应方法
    # =============== Demo2 end ====================
    
    
    """
    Demo2 的方法虽实现了第二个场景的需求,但是不够好,
    只是在对象方法在调用是才抛出异常,如果想要在对象在
    初始化就抛出异常,就需要使用我们的abc模块了。
    """
    
    
    # ================= Demo3 start ===================
    # 使用全局的abc模块
    import abc
    
    
    class CacheBase(metaclass=abc.ABCMeta):
        
        @abc.abstractmethod
        def get(self, key):
            pass
        
        @abc.abstractmethod
        def set(self, key, value):
            raise NotImplementedError
            
            
    class RedisCache(CacheBase):
        pass
    
    
    redis_cache = RedisCache()  # 抛出异常
    # ================= Demo3 end ======================
    

    3.3 使用instance而不是type

    class A:
        pass
    
    
    class B(A):
        pass
    
    
    b = B()
    print(isinstance(b, B))  # True
    print(isinstance(b, A))  # True
    
    
    print(type(b) is B)  # is 判断是否是同一个对象
    print(type(b) == B)  # == 判断的是值是否相等
    
    print(type(b) is A)  # False
    
    
    """
    注意isinstance比使用type好,type无法找到父类,
    而isinstance可以。
    同时注意 == 与 is 的区别。
    """
    
    

    3.4 类变量和对象变量

    # =========== Demo1 start ==============
    class A:
        aa = 1  # 类变量
        
        def __init__(self, x, y):
            self.x = x
            self.y = y
            
    
    a = A(2, 3)
    print(a.x, a.y, a.aa)  # 2 3 1
    print(A.aa)  # 1
    print(A.x)  # 抛出异常
    # =========== Demo1 end ================
    
    
    """
    在 Demo1 中打印a.aa时,首先会在对象属性中查找,
    若是找不到则在类属性中查找。以上 Demo 很好理解。
    """
    
    
    # ============ Demo2 start =================
    class A:
        aa = 1  # 类变量
        
        def __init__(self, x, y):
            self.x = x
            self.y = y
            
            
    a = A(2, 3)
    A.aa = 11
    print(a.x, a.y, a.aa)  # 2 3 11
    A.aa = 111
    a.aa = 100
    print(a.x, a.y, a.aa)  # 2 3 100
    # ============= Demo2 end ==================
    
    
    """
    在对A.aa与a.aa同时赋值时,此时,对象属性中
    就有了aa属性,所以在打印a.aa时,就会首先打
    印对象里的属性啦。注意这个细节哦。类与实例的
    变量是两个独立的存在。
    """
    
    
    # 在Demo3中加入以下代码
    b = A(3, 4)
    print(b.aa)  # 3 4 111
    
    
    """
    可见类变量是所有实例共享的。
    """
    

    3.5 类属性和实例属性以及查找顺序

    """
    属性就是在类或实例中定义的变量或方法。
    """
    
    
    class A:
        name = "A"
        def __init__(self):
            self.name = "obj"
            
            
    a = A()
    print(a.name)  # obj
    
    """
    在单继承这很简单,但是在多继承下,这些就会变得复杂起来。
    """
    
    

    MRO算法

    Method Relation order

    Python3使用的算法是C3,以下算法是Python早些版本的属性查找算法,均存在一些缺陷,下面一一介绍。

    深度优先搜索

    MRO算法-1

    上图的继承关系中使用深度优先搜索是没有问题的,但是要是继承关系是菱形,如下图所示就会出现问题。需要使用广度优先搜索算法,使得继承顺序为A->B->C->D。

    问题就是,下图中,如果C里的方法重写了D的方法。但是由于深度优先搜索算法会首先查找D中的属性,那么C的重写方法就不会生效。所有需要使用广度优先搜索算法解决问题。

    MRO算法-2

    广度优先

    广度优先虽然解决了上述问题,但是呢,若果出现如下继承关系,广度优先算法又出现问题了。就是,如果D,C都有一个同名的方法,而继承D的B没有实现这个同名方法。那么在搜索完B时,应该搜索D,但是广度优先算法回去搜索C,这逻辑上是不合理的。

    MRO算法-3

    所以Python3统一成了一种方法,C3使得这些问题都不复存在。

    # =============== 菱形继承问题 ==================
    #新式类
    class D:
        pass
    
    
    class B(D):
        pass
    
    
    class C(D):
        pass
    
    
    class A(B, C):
        pass
    
    
    print(A.__mro__)
    """
    结果:
    (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
    """
    
    
    # ================ 非菱形继承问题 =================
    class D:
        pass
    
    
    class B(D):
        pass
    
    
    class E:
        pass
    
    
    class C(E):
        pass
    
    
    class A(B, C):
        pass
    
    
    print(A.__mro__)
    """
    结果:
    (<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class '__main__.E'>, <class 'object'>)
    """
    
    

    3.6 静态方法、类方法以及对象方法以及参数

    class Date:
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
        
        def tomorrow(self):
            self.day += 1
            
    	@staticmethod
        def parse_from_string(data_str):
            year, month, day = tuple(date_str.split("-"))
            return Date(int(year), int(month), int(day))  # 出现硬编码情况
        
        @classmethod
        def from_string(cls, date_str):
            year, month, day = tuple(date_str.split("-"))
            return cls(int(year), int(month), int(day))  # 解决硬编码
            
    	def __str__(self):
            return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
        
        
    if __name__ == "__main__":
        new_day = Date(2020, 5, 7)
        new_day.tomorrow()
        print(new_day)
        date_str = "2020-5-7"
        new_day = Date.parse_from_string(date_str)
        print(new_day)
    

    3.7 数据封装和私有属性

    class Date:
        #构造函数
        def __init__(self, year, month, day):
            self.year = year
            self.month = month
            self.day = day
    
        def tomorrow(self):
            self.day += 1
    
        @staticmethod
        def parse_from_string(date_str):
            year, month, day = tuple(date_str.split("-"))
            return Date(int(year), int(month), int(day))
    
        @staticmethod
        def valid_str(date_str):
            year, month, day = tuple(date_str.split("-"))
            if int(year)>0 and (int(month) >0 and int(month)<=12) and (int(day) >0 and int(day)<=31):
                return True
            else:
                return False
    
        @classmethod
        def from_string(cls, date_str):
            year, month, day = tuple(date_str.split("-"))
            return cls(int(year), int(month), int(day))
    
        def __str__(self):
            return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
    
    
    class User:
        def __init__(self, birthday):
            self.__birthday = birthday  # _User__birthday
            
        def get_age(self):
            return 2018 - self.__birthday.year
        
        
    if __name__ == "__main__":
        user = User(Date(1990, 2, 1))
        print(user.birthday))
        print(user.get_age())
        
    

    3.8 Python对象自省机制

    """
    自省就是通过一定的机制查询到对象的内部结构。
    """
    
    class Person:
        name = "User"
        
        
    class Student(Person):
        """
        文档
        """
        def __init__(self, school_name):
            self.school_name = school_name
            
            
    if __name__ == "__main__":
        stu = Student("家里蹲")
        print(stu.__dict__)  # {'school_name': '家里蹲'}
        print(stu.name)  # User
        """
        stu的属性字典里没有name,那么是怎么能够得到User的呢?
        实际上这个name在Person的属性字典里,类也是对象嘛!!
        stu没有解释器就往上层找
        """
        print(Person.__dict__)  # {'__module__': '__main__', 'name': 'User', ...}
        print(Student.__dict__)  # {... '__doc__': '
        文档
        ', ... }
        print(dir(stu))  # ['__class__', '__delattr__', '__dict__', '__dir__', ...]
        stu.__dict__["city"] = "WC"
        print(stu.city)  # WC
    

    3.9 super函数

    """
    super函数并没有那么简单...
    """
    
    class A:
        def __init__(self):
            print("A")
            
            
    class B(A):
        def __init__(self):
            print("B")
            # super(B, self).__init__()  # python2
            super().__init__()
            
    
    class C(A):
        def __init__(self):
            print("C")
            super().__init__()
            
            
    class D(B, C):
        def __init__(self):
            print("D")
            super(D, self).__init__()
            
            
    # 既然我们重写了B的构造函数,为什么还要去调用super?
    """
    为了能够重用父类的一些方法,避免编写重复的逻辑
    """
    
    # super到底执行顺序什么样?
    """
    super并不是仅仅调用父类方法....
    """
    if __name__ == "__main__":
        d = D()
        """
        直观结果:
        D
        B
        A
        """
        
        """
        实际结果:
        D
        B
        C
        A
        """
        
        
    # 所以super的查找顺序是根据mro顺序来的
    	print(D.__mro__)
        
    

    3.10 Django rest framework 中对多继承使用的经验

    Mixin模式

    1. Mixin功能单一
    2. 不和基类关联,可以和任意基类组合,基类可以不和mixin关联就能初始化
    3. 在mixin中不要使用super这种用法

    3.11 Python中的with语句

    """
    try expect finally 的用法
    """
    
    
    # ============== Demo1 start ====================
    try:
        print("code started")
    	raise KeyError
    except KeyError as e:
        print("key error")
    else:  # 没有异常再执行
        print("other code")
    finally:
        print("finally")  # 不管怎么样该行代码都会运行,用于关闭文件对象等
    # ============== Demo1 end =====================
    
    
    # ================== Demo2 start ========================
    def exe_try():
        try:
            print("code start")
            raise KeyError
            return 1
        except KeyError as e:
            print("Key error")
            return 2
        else:
            print("other error")
            return 3
        finally:
            print("finally")
            return 4
        
        
    if __name__ == "__main__":
        result = exe_try()
        print(result)
        
    """
    result 的结果会是什么呢?
    答案是: 4
    那么注释 return 4
    结果又是什么呢?
    答案是: 2
    
    因为每次执行到return语句时,
    其值都会压入栈中,最终去栈顶的值。
    """
    # ================== Demo2 end ==========================
    

    上下文管理协议

    """
    基于:
    __enter__(self)
    __exit__(self, exc_type, exc_val, exc_tb)
    """
    
    
    class Sample:
        def __enter__(self):
            # 获取资源
            print("enter")
            return self
        
        def __exit__(self, exc_type, exc_val, exc_tb):
            # 释放资源
            print("exit")
            
    	def do_something(self):
            print("doing something")
            
            
    with Sample() as sample:
        sample.do_something()
        
        
    """
    执行结果:
    enter
    doing something
    exit
    """
    

    3.12 contextlib实现上下文管理器

    import contextlib
    
    @contextlib.contextmanager
    def file_open(file_name):
        print("file open")
        yield {}
        print("file end")
        
        
    with file_open("bobby.txt") as f_opened:
        print("file processing")
        
        
    """
    执行结果:
    file open
    file processing
    file end
    """
    
  • 相关阅读:
    Ext.Net学习笔记02:Ext.Net用法概览
    Ext.Net学习笔记01:在ASP.NET WebForm中使用Ext.Net
    【转】好的用户界面-界面设计的一些技巧
    发布mvc报错:403.14-Forbidden Web 服务器被配置为不列出此目录的内容
    抢票季:吐槽12306 & 分享抢票经验
    2.5星|《解谜茑屋》:疑似企业公关稿,对话体,信息含量较低
    樊登推荐过的书15本,好书2本半
    一些黑猩猩会使用草药治病,疗效还不错:3.5星|邓巴《人类的算法》
    莫奈塞尚的知名度,主要归功于富豪画家卡耶博特的遗赠:4星|《引爆流行》
    4星|《猎药师》:五千年以来药物研发简史,作者是前一线科学家
  • 原文地址:https://www.cnblogs.com/xunjishu/p/12843582.html
Copyright © 2011-2022 走看看