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
    """
    
  • 相关阅读:
    Spring boot unable to determine jdbc url from datasouce
    Unable to create initial connections of pool. spring boot mysql
    spring boot MySQL Public Key Retrieval is not allowed
    spring boot no identifier specified for entity
    Establishing SSL connection without server's identity verification is not recommended
    eclipse unable to start within 45 seconds
    Oracle 数据库,远程访问 ora-12541:TNS:无监听程序
    macOS 下安装tomcat
    在macOS 上添加 JAVA_HOME 环境变量
    Maven2: Missing artifact but jars are in place
  • 原文地址:https://www.cnblogs.com/xunjishu/p/12843582.html
Copyright © 2011-2022 走看看