# 第四章:深入类和对象
# 4.1 鸭子类型和多态
# 鸭子类型
当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来像鸭子,那么这只鸟可以被称为鸭子
class Cat:
def say(self):
print('I am a cat.')
class Dog:
def say(self):
print('I am a dog.')
class Duck:
def say(self):
print('I am a duck.')
# Python中较灵活,只要实现say方法就行,实现了多态
animal = Cat
animal().say()
# 实现多态只要定义了相同方法即可
animal_list = [Cat, Dog, Duck]
for an in animal_list:
an().say()
"""
class Animal:
def say(self):
print('I am a animal.')
# 需要继承Animal,并重写say方法
class Cat(Animal):
def say(self):
print('I am a cat.')
# Java 中定义需要指定类型
Animal an = new Cat()
an.say()
"""
li1 = ['i1', 'i2']
li2 = ['i3', 'i4']
tu = ('i5', 'i6')
s1 = set()
s1.add('i7')
s1.add('i8')
# 转变观念,传入的不单单是list,甚至自己实现 iterable 对象
li1.extend(li2) # iterable
li1.extend(tu)
li1.extend(s1)
print(li1)
- 实现多态只要定义了相同方法即可
- 魔法函数充分利用了鸭子类型的特性,只要把函数塞进类型中即可
# 4.2 抽象基类(abc模块)
# 抽象基类
- 抽象基类无法实例化
- 变量没有类型限制,可以指向任何类型
- 抽象基类和魔法函数构成了python的基础,即协议
在抽象基类定义了抽象方法,继承了抽象基类的类,必须实现这些方法
场景一:想判断某个对象的类型
# 检查某个类是否有某种方法
class Company:
def __init__(self, name):
self.name = name
def __len__(self):
return len(self.name)
company = Company('Linda Process Ltd.')
print(hasattr(company, '__len__'))
from collections.abc import Sized
print(isinstance(company, Sized))
场景二:强制子类必须实现某些方法
import abc
class CacheBase(metaclass=abc.ABCMeta):
@abc.abstractmethod
def get(self, key):
pass
@abc.abstractmethod
def set(self, key, value):
pass
class MemoryCache(CacheBase):
pass
注意:抽象基类容易设计过度,多继承推荐使用Mixin
# 4.3 isinstance 和 type 的区别
- isinstance 会去查找继承链
- type 只判断变量的内存地址
class A:
pass
class B(A):
pass
b = B()
print(isinstance(b, B))
print(isinstance(b, A))
# is 判断 id 的意思
print(type(b) is B)
print(type(b) is A) # False
# 4.4 类变量与实例变量
- 类变量定义与使用
- 实例变量定义与使用
- 类变量是所有实例变量共享
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)
A.aa = 111
a.aa = 100 # 新建一个a的属性aa, 100赋值给该aa
print(A.aa, a.aa)
# 4.5 类属性和实例属性以及查找顺序
# 类属性和实例属性
- 类属性:定义在类中的变量和方法
- 实例属性:__init__中定义
# 深度优先 DFS
- 查找顺序为 A -> B -> D -> C -> E
- 此种场景深度优先较为合适
- 查找顺序为 A -> B -> D -> C
- 此种场景 当C中重载了D中某个方法,该查找顺序就不合适
# 广度优先 BFS
- 查找顺序为 A -> B -> C -> D
- 此种场景深度优先较为合适
- 查找顺序为 A -> B -> C -> D -> E
- 此种场景 B继承D,B和D是一体的,D应该先于C
# MRO C3 算法
菱形功能继承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'>)
B、C 各自继承D、E场景
class D:
pass
class E:
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'>)
# 4.6 静态方法、类方法、对象方法以及参数
- 静态方法 @staticmethod
- 类方法 @classmethod
- 实例方法
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 date_from_str(date_str):
year, month, day = tuple(date_str.split('-'))
return Date(int(year), int(month), int(day))
@classmethod
def date_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, 2, 20)
new_day.tomorrow()
print(new_day)
date_str = '2020-12-12'
print(Date.date_from_str(date_str))
print(Date.date_from_string(date_str))
# 4.7 数据封装和私有属性
定义类时双下划线的属性,为私有属性
class User:
def __init__(self):
self.__age = 18
def get_age(self):
return self.__age
if __name__ == '__main__':
user = User()
print(user.get_age())
# print(user.__age)
# _class__attr, 做了变形
print(user._User__age)
- python并不能严格限制私有属性的使用,这是一种写代码规范
# 4.8 python对象的自省机制
通过一定的机制查询对象的内部结构
- dict
- dir()
class User:
name = 'user'
class Student(User):
def __init__(self):
self.school_name = 'school'
if __name__ == '__main__':
stu = Student()
# 通过__dict__ 查询属性, C语言实现,经过优化,较快
print(stu.__dict__)
stu.__dict__['age'] = 18
print(stu.age)
print(User.__dict__)
print(dir(stu))
← 第三章:魔法函数 第五章:自定义序列类 →