目录
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 本章小结
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()
动态与静态语言:参考博文:https://www.cnblogs.com/fishshadow/p/10920290.html
覆盖,无法实例化
用到抽象基类的几种情况:
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__"))
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))
子类不实现某些方法就会报错
实现了一个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方法:所以输出结果:
补充: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不会
实例变量为某一个具体的实例所有,类变量为类的所有实例所共有,实例优先使用自己的变量,如果查找不到,则向上查找,即去类中查找
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)
输出结果:
方法查找顺序
深度优先搜索 DFS;Depth first search;degree first serch;Deep first search
输入:
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中,就会加长搜索时间
广度优先算法:
广度优先算法也会出问题,比如要查找的方法在D中,如果c中有同名的,D就会被C覆盖掉。
因此产生了现在使用的C3算法:
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)
私有属性,无法通过实例化对象和子类访问,只能通过类中的公共方法访问
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)
自省是通过一定的机制查询到对象的内部结构--------案例:通过__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))
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()
mixin模式特点 1. Mixin类功能单一 2. 不和基类关联,可以和任意基类组合, 基类可以不和mixin关联就能初始化成功 3. 在mixin中不要使用super这种用法 4. 使用mixmin这种模式,尽量带mixmin后缀(约定俗成,方便别人查看)
复习知识:
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()
当函数被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")