Python高级编程和异步IO并发编程---学习笔记---第四章( 深入类和对象)

目录

 

4-1 鸭子类型和多态

4-2 与4-3:抽象基类(abc模块)

(1)我们去检查某个类是否有某种方法

(2)我们在某些情况之下希望判定某个对象的类型

(3)我们需要强制某个子类必须实现某些方法

4-4 isinstance和type的区别

4-5 类变量和实例变量

4-6 类和实例属性的查找顺序—mro查找(Method resolution order)

4-7 类方法、静态方法和实例方法

一,实例方法:

二,静态方法,初始化操作

三,类方法

4-8 数据封装和私有属性

4-9 python对象的自省机制

4-10 super真的是调用父类吗?

4-11 mixin继承案例-django rest framework

4-12 python中的with语句

上下文管理器协议:

4-13 contextlib简化上下文管理器

4-14 本章小结

 


4-1 鸭子类型和多态

duck typing      “当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像zd鸭子,那么这只鸟就可以被称为鸭子。”
我们并不关心对象是什么类型,到底是不是鸭子,只关心行为。”在鸭子类型中,关注的不是对象的类型本身,而是它是如何使用的。
也就是说,在python语言中,因为python 并不强调类型,所以只要有一样行为,程权序并不关心是不是想要的对象。

鸭子类型参考博文:https://www.jianshu.com/p/e97044a8169a

多态参考博文:https://www.cnblogs.com/luchuangao/p/6739557.html

实现多态(同一个方法,不同输出)的代码:

java继承后需要重写,python不用

class Cat(object):
    def say(self):
        print("i am a cat")

class Dog(object):
    def say(self):
        print("i am a fish")
class Duck(object):
    def say(self):
        print("i am a duck")

animal_list = [Cat, Dog, Duck]
for animal in animal_list:
    animal().say()

4-2 与4-3:抽象基类(abc模块)

动态与静态语言:参考博文:https://www.cnblogs.com/fishshadow/p/10920290.html

覆盖,无法实例化

用到抽象基类的几种情况:

(1)我们去检查某个类是否有某种方法

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __len__(self):
        return len(self.employee)


com = Company(["bobby1","bobby2"])
print(hasattr(com, "__len__"))

(2)我们在某些情况之下希望判定某个对象的类型

class Company(object):
    def __init__(self, employee_list):
        self.employee = employee_list

    def __len__(self):
        return len(self.employee)


com = Company(["bobby1","bobby2"])
from collections.abc import Sized
print(isinstance(com, Sized))
print(len(com))

(3)我们需要强制某个子类必须实现某些方法

子类不实现某些方法就会报错

实现了一个web框架,集成cache(redis, cache, memorychache)
需要设计一个抽象基类, 指定子类必须实现某些方法     如何去模拟一个抽象基类

import abc
from collections.abc import *
class CacheBase(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def get(self, key):
        pass

    @abc.abstractmethod
    def set(self, key, value):
        pass
class RedisCache(CacheBase):
    def set(self, key, value):
        pass
redis_cache = RedisCache()
redis_cache.set("key", "value")

因为子类没实现get方法:所以输出结果:

4-4 isinstance和type的区别

补充:is和==有区别

is是判断id是否相同,即是否为同一个对象   ==是判断值是否相等

class A:
    pass
class B(A):
    pass
b = B()
print(isinstance(b, B))
print(isinstance(b, A))
print(type(b)is type(A))

输出结果:

isinstance会自动去查找继承链,而type不会

4-5 类变量和实例变量

实例变量为某一个具体的实例所有,类变量为类的所有实例所共有,实例优先使用自己的变量,如果查找不到,则向上查找,即去类中查找

class A:
    aa = 1
    def __init__(self, x, y):
        self.x = x
        self.y = y

a = A(2,3)

A.aa = 11
a.aa = 100
print(a.x, a.y, a.aa)
print(A.aa)

b = A(3,5)
print(b.aa)

输出结果:

4-6 类和实例属性的查找顺序—mro查找(Method resolution order)

方法查找顺序

深度优先搜索 DFS;Depth first search;degree first serch;Deep first search

Python高级编程和异步IO并发编程---学习笔记---第四章( 深入类和对象)_第1张图片Python高级编程和异步IO并发编程---学习笔记---第四章( 深入类和对象)_第2张图片

输入:

class D:
    pass

class E:
    pass

class C(E):
    pass

class B(D):
    pass

class A(B, C):
    pass

print(A.__mro__)

第一个图输出(深度优先):

经测试:第二个图,现在使用了广度优先(图片未标注)

输入:

class D:
    pass

class C(D):
    pass

class B(D):
    pass

class A(B, C):
    pass

print(A.__mro__)

输出:

深度优先第二种不太合理:因为bc同级,如果要查找的在C中,就会加长搜索时间

广度优先算法:

Python高级编程和异步IO并发编程---学习笔记---第四章( 深入类和对象)_第3张图片

广度优先算法也会出问题,比如要查找的方法在D中,如果c中有同名的,D就会被C覆盖掉。

因此产生了现在使用的C3算法:

4-7 类方法、静态方法和实例方法

一,实例方法:

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def tomorrow(self):
        self.day += 1
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month,day=self.day)
if __name__ == "__main__":
    new_day = Date(2018, 12, 31)
    new_day.tomorrow()
    print(new_day)

字符串的拆分:

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def tomorrow(self):
        self.day += 1
    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)

date_str = "2018-12-31"
year, month, day = tuple(date_str.split("-"))
new_day = Date(int(year), int(month), int(day))
print (new_day)

二,静态方法,初始化操作

总结了四个特点:

1用@staticmethod声明

2虽然在类里面定义,但定义时并不需要self参数

3使用类名调用,不用将类实例化后再调用

4返回值可以是类的实例化对象

(注意,魔法函数__str__在实例化时会自动调用)

 

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))

    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)

date_str = "2018-12-31"
new_day = Date.parse_from_string(date_str)
print (new_day)

优点:调用不需要实例化这个过程,简便

缺点:如果类名更改了,返回实例化对象时,也要跟着改名字

不需要返回实例化对象,只需要判断输入数据是否合法时就可以经常使用静态方法,如下:

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 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

    def __str__(self):
        return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)


date_str = "2018-12-32"
#用classmethod完成初始化
new_day = Date.valid_str(date_str)
print(new_day)

 

三,类方法

1用@classmethod声明

2在类里面定义,定义时需要clf(类)参数,clf代表类,但可以自由更改,使用clf只是约定俗成

3使用类名调用,不用将类实例化后再调用(与静态相同)

4返回值可以是类的实例化对象(与静态相同)

(注意,魔法函数__str__在实例化时会自动调用)

优点:更改类名不需要更改实例化对象处的clf,调用不需要实例化这个过程,简便

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
        

    def tomorrow(self):
        self.day += 1

    @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)


date_str = "2018-12-31"
#用classmethod完成初始化
new_day = Date.from_string(date_str)
print(new_day)

4-8 数据封装和私有属性

私有属性,无法通过实例化对象和子类访问,只能通过类中的公共方法访问

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
class User:
    def __init__(self, birthday):
        self.__birthday = birthday

    def get_age(self):
        #返回年龄
        return 2018 - self.__birthday.year


if __name__ == "__main__":
    user = User(Date(1990,2,1))

    print(user.get_age())

bug:可以通过:实例化对象._类名__属性名访问

注意细节,第二个类实例化时传入了一个实例化对象参数给构造函数(对应birthday)

(一个对象成为了另一个对象的属性)

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
class User:
    def __init__(self, birthday):
        self.__birthday = birthday

    def get_age(self):
        #返回年龄
        return 2018 - self.__birthday.year


if __name__ == "__main__":
    user = User(Date(1990,2,1))
    print(user._User__birthday.year)

4-9 python对象的自省机制

自省是通过一定的机制查询到对象的内部结构--------案例:通过__dict__查询当前对象所属类的属性,以字典的形式给出属性和属性值,不可向上查找,而通过object.attribute可以向上查找
class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
class Person:
    """
    人
    """
    name = "user"

class Student(Person):
    def __init__(self, scool_name):
        self.scool_name = scool_name

if __name__ == "__main__":
    user = Student("慕课网")
    print("student类的属性有:")
    print(user.__dict__)
    print("Person类的属性有:")
    print(Person.__dict__)

还可以给当前对象所属的类增加属性值:

class Date:
    #构造函数
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day
class Person:
    """
    人
    """
    name = "user"

class Student(Person):
    def __init__(self, scool_name):
        self.scool_name = scool_name
if __name__ == "__main__":
    user = Student("慕课网")
    print("没有增加属性前:")
    print(user.__dict__)
    print("增加属性后:")
    user.__dict__["school_addr"] = "北京市"
    print(user.__dict__)
    print("增加的确实可以访问:")
    print(user.school_addr)

查看当前对象的所有属性dir---会向上查找,还会找出内置的一些属性:

class Person:
    """
    人
    """
    name = "user"

class Student(Person):
    def __init__(self, scool_name):
        self.scool_name = scool_name
if __name__ == "__main__":
    user = Student("慕课网")
    print(dir(user))

4-10 super真的是调用父类吗?

super可以调用被继承的的类的构造方法,这样就不用重写了,减少工作量

super其实是依次调用的mro(方法查找顺序)中所有查找到的类的构造函数(即如果是多继承,或者是父类还有父类,就按mro中查找出来的顺序,依次全部调用,如果只有一个父类,则调用的就是父类的构造函数)

class A:
    def __init__(self):
        print("A")


class B(A):
    def __init__(self):
        print("B")
        super().__init__()


class C(A):
    def __init__(self):
        print("C")
        super().__init__()


class D(B, C):
    def __init__(self):
        print("D")
        super().__init__()


if __name__ == "__main__":
    print("D的mro顺序是:")
    print(D.__mro__)
    print("输出查找结果")
    d = D()

Python高级编程和异步IO并发编程---学习笔记---第四章( 深入类和对象)_第4张图片

 

4-11 mixin继承案例-django rest framework

mixin模式特点
1. Mixin类功能单一
2. 不和基类关联,可以和任意基类组合, 基类可以不和mixin关联就能初始化成功
3. 在mixin中不要使用super这种用法
4. 使用mixmin这种模式,尽量带mixmin后缀(约定俗成,方便别人查看)

4-12 python中的with语句

复习知识:

try:

抛出可能的异常(没有异常则正常执行,有异常的话中断执行异常之后的语句)

except:

捕获到定义的异常时执行

else:

无异常时else block执行

finally:

无论try语句是否有异常,最后都要执行的代码。(如果try语句有错,先执行完finally block, 然后回到try block报错。)

参考博文:https://blog.csdn.net/m0_37822685/article/details/80259402

上下文顺序(finally中的return优先:如果finally中有return,则执行finally的语句,没有则执行其他的return----提示:return的放进堆栈了):

def exe_try():
    try:
        print ("code started")
        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)

 

上下文管理器协议:

当一个类中有有 __enter__和__exit__这两个魔法函数时,该类就被自动赋予了上下文管理器协议,协议内容:可以使用with语句实例化,执行with语句时,先自动调用第一个魔法函数,然后执行with语句内部的内容,最后执行第二个魔法函数的内容,因此,第一个魔法函数可以用来释放资源(f.close()

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()

4-13 contextlib简化上下文管理器

当函数被contextlib.contextmanager装饰后,yield生成器之前的内容就相当于魔法函数__enter__中的内容,yield生成器之后的内容就相当魔法函数__exit__中的内容

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")

 

4-14 本章小结

Python高级编程和异步IO并发编程---学习笔记---第四章( 深入类和对象)_第5张图片

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(学习笔记,python)