深入类和对象
一、鸭子类型和多态
1、定义:当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。
2、示例:
(1) 示例一:
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()
运行结果:
I am a cat
I am a dog
I am a duck
(2) 示例二:
class Cat(object):
def say(self):
print("I am a cat")
class Dog(object):
def say(self):
print("I am a dog")
def __getitem__(self, item):
return "dog"
class Duck(object):
def say(self):
print("I am a duck")
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list
def __getitem__(self, item):
return self.employee[item]
def __len__(self):
return len(self.employee)
company = Company(["tom", "bob", "jane"])
a = ["麻花", "米线"]
b = ["小麻花", "大碗米线"]
name_tuple = ("大麻花", "小碗米线")
name_set = set()
name_set.add("大份麻花")
name_set.add("小碗粗米线")
a.extend(b)
print(a)
a.extend(name_tuple)
print(a)
a.extend(name_set)
print(a)
a.extend(company)
print(a)
dog = Dog()
a.extend(dog)
print(a)
运行结果:
['麻花', '米线', '小麻花', '大碗米线']
['麻花', '米线', '小麻花', '大碗米线', '大麻花', '小碗米线']
['麻花', '米线', '小麻花', '大碗米线', '大麻花', '小碗米线', '小碗粗米线', '大份麻花']
['麻花', '米线', '小麻花', '大碗米线', '大麻花', '小碗米线', '小碗粗米线', '大份麻花', 'tom', 'bob', 'jane']
二、抽象基类(abc模块)
# 我们去检查某个类是否有某种方法
class Company(object):
def __init__(self, employee_list):
self.employee = employee_list
def __len__(self):
return len(self.employee)
company = Company(["麻花", "米线"])
print(hasattr(company, "__len__"))
# 运行结果:
True
# 我们在某些情况下希望判定某个对象的类型
from collections.abc import Sized
isType = isinstance(company, Sized)
print(isType)
# 运行结果:
True
# 我们需要实现强制某个子类必须实现某些方法
# 实现一个web框架,集成cache(redis, cache, memorychache)
# 需要设计一个抽象基类,指定子类必须实现某些方法
# 如何去模拟一个抽象基类
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, key):
pass
@ abc.abstractmethod
def set(self, key):
pass
class RedisCache(CacheBase):
def set(self, key, value):
pass
redis_cache = RedisCache()
# 运行结果:
Traceback (most recent call last):
File "/home/caoxuejin/Project/sample/chapter13/c4.py", line 47, in <module>
redis_cache = RedisCache()
TypeError: Can't instantiate abstract class RedisCache with abstract methods get
三、isinstace和type的区别
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b, B))
print(isinstance(b, A))
运行结果:
True
True
print(type(b))
print(type(b) is B)
print(type(b) is A)
运行结果:
True
False
四、类变量和实例变量
class A(object):
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
A.aa = 11
a.aa = 100
print(a.x, a.y, a.aa)
print(A.aa)
运行结果:
2 3 100
11
b = A(4, 5)
print(b.x, b.y, b.aa)
运行结果:
4 5 11
五、类和实例属性的查找顺序——mro查找
1、示例一:
class A:
name = "麻花"
a = A()
print(a.name)
运行结果:
麻花
class A:
name = "麻花"
def __init__(self):
self.name = "米线"
a = A()
print(a.name)
运行结果:
米线
2、mro查找:
# A>>B>>C>>D
# 新式类
class D:
pass
class C(D):
pass
class B(D):
pass
class A(B, C):
pass
print(A.__mro__)
运行结果:
(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>)
# A>>B>>D>>C>>E
class E:
pass
class D:
pass
class C(E):
pass
class B(D):
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'>)
六、类方法、静态方法和实例方法
from datetime import datetime
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("-"))
new_day = Date(int(year), int(month), int(day))
return new_day
@staticmethod
def valid_str(date_str):
try:
datetime.strptime(date_str, "%Y-%m-%d")
return True
except ValueError:
return False
@classmethod
def from_string(cls, date_str):
year, month, day = tuple(date_str.split("-"))
new_day = cls(int(year), int(month), int(day))
return new_day
def __str__(self):
return "{year}/{month}/{day}".format(year=self.year, month=self.month, day=self.day)
if __name__ == "__main__":
new_day = Date(2019, 10, 6)
print(new_day)
# 运行结果:
2019/10/6
new_day.tomorrow()
print(new_day)
# 运行结果:
2019/10/7
date_str = "2019-10-06"
year, month, day = tuple(date_str.split("-"))
new_day = Date(int(year), int(month), int(day))
print(new_day)
# 运行结果:
2019/10/6
# 使用staticmethod完成初始化
new_day = Date.parse_from_string(date_str)
print(new_day)
# 运行结果:
2019/10/6
# 使用classmethod完成初始化
new_day = Date.from_string(date_str)
print(new_day)
# 运行结果:
2019/10/6
new_day = Date.valid_str(date_str)
print(new_day)
# 运行结果:
True
七、数据封装和私有属性
class Date:
# 构造函数
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = 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
def get_age(self):
# 返回年龄
return 2019 - self.__birthday.year
if __name__ == "__main__":
user = User(Date(1993, 12, 12))
print(dir(user))
# 运行结果:
['_User__birthday', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'get_age']
print(user._User__birthday)
# 运行结果:
1993/12/12
print(user.get_age())
# 运行结果:
26
八、python对象的自省机制
class Person:
"""
人员信息
"""
name = "麻花"
class Student(Person):
def __init__(self, school_name):
self.school_name = school_name
if __name__ == "__main__":
user = Student("光明小学")
# 通过__dict__查询属性
print(user.__dict__)
# 运行结果:
{'school_name': '光明小学'}
user.__dict__["school_addr"] = "北京市"
print(user.school_addr)
# 运行结果:
北京市
print(Person.__dict__)
# 运行结果:
{'__module__': '__main__', '__doc__': '\n 人员信息\n ', 'name': '麻花', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>}
print(user.name)
# 运行结果:
麻花
# dir() 查看对象的所有属性
print(dir(user))
# 运行结果:
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'school_addr', 'school_name']
a = [1, 2, 3]
print(dir(a))
# 运行结果:
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
九、super真的是调用父类吗?
from threading import Thread
class MyThread(Thread):
def __init__(self, name, user):
self.user = user
super.__init__(name=name)
# 既然我们重写B的构造函数,为什么还要去调用super?
# super到底执行顺序是什么样的?
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(D, self).__init__()
if __name__ == "__main__":
d = D()
# 运行结果:
D
B
C
A
print(D.__mro__)
# 运行结果:
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
十、mixin继承案例——django rest framework
1、mixin类功能单一;
2、不和基类关联,可以和任意基类组合,基类可以不和mixin关联就能初始化成功;
3、在mixin中不要使用super这种用法。
十一、python中的with语句
1、try---finally:
# 示例一:
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)
# 运行结果:
code started
key error
finally
4
# 示例二:
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)
# 运行结果:
code started
key error
finally
2
2、with语句:
# 上下文管理器协议,重写魔法函数__enter__和__exit__
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
十二、contextlib简化上下文管理器
import contextlib
@contextlib.contextmanager
def file_open(file_name):
print("file open")
yield {}
print("file end")
with file_open("麻花.txt") as fp:
print("file processing")
# 运行结果:
file open
file processing
file end