一、Mixins机制
是一种命名规范,解决多继承中一个对象同时属于多个类的问题,Mixins机制的核心是在多继承背景下尽可能的提升代码的可读性,符合人类的思维:什么是什么。
拿交通工具来说,民航飞机、直升飞机、汽车都是属于交通工具。可以定义一个交通工具的父类,三者都可以继承这个父类,他们都有载客的功能。除了载客,飞机还能起飞、上天!!!,而汽车不行。显然民航飞机和直升机的飞行这个属性只是这两个交通工具的所特有的功能而已,在继承交通工具父类的同时可以用Mixins机制混入飞行这个功能,注意混入的只是一个功能(多个功能写多个Mixin)不是两者的父类;而汽车直接继承交通工具父类。这样的话,民航飞机、直升飞机、汽车都是交通工具的子类,都能载客,而且还为民航飞机和直升飞机附加了飞行这个属性。满足了什么是什么的思维习惯,三者都是交通工具,尽可能的减少代码的冗余。
# 定义交通工具父类
class Vehicle:
def carry(self):
print('我是交通工具,世界那么大你想去哪儿都需要我')
# 定义一个有分行功能的类,用Mixins规范命名,标识其不是父类,用于混入。
class FlyableMixin:
def fly(self):
print('我能飞行,我可以上天!')
# 民航飞机
class CivilAircraft(FlyableMixin, Vehicle):
pass
# 直升机
class Helicopter(FlyableMixin, Vehicle):
pass
# 汽车
class Car(Vehicle):
pass
civilAircraft_obj = CivilAircraft()
helicopter_obj = Helicopter()
car_obj = Car()
# 三者有载客的属性
civilAircraft_obj.carry() # 我是交通工具,世界那么大你想去哪儿都需要我
helicopter_obj.carry() # 我是交通工具,世界那么大你想去哪儿都需要我
car_obj.carry() # 我是交通工具,世界那么大你想去哪儿都需要我
# 民航飞机和直升机有飞行的功能
civilAircraft_obj.fly() # 我能飞行,我可以上天!
helicopter_obj.fly() # 我能飞行,我可以上天!
补充:通常Mixin结果的类放在左边
二、在子类派生的新方法中重用父类功能的方式
方式一:
指名道姓的调用某一类下的函数,不依赖于继承关系
class OldboyPeople:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
class Teacher(OldboyPeople):
def __init__(self, name, age, sex, level, salary):
# 指名道姓的向 OldboyPeople类中要.__init__函数
OldboyPeople.__init__(self, name, age, sex)
self.level = level
self.salary = salary
tea_obj = Teacher('egon', 18, 'male', 10, 3000)
print(tea_obj.__dict__)
# {'name': 'egon', 'age': 18, 'sex': 'male', 'level': 10, 'salary': 3000}
方式二:
super()调用父类提供给直接的方法,严格的依赖继承关系,调用super()会得到一个特殊的对象,该对象会参照发起属性查找的那个类的mro列表去查找,找到列表中当前类的父类,从父类中找属性。
class OldboyPeople:
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
def f1(self):
print(f'你好呀!{self.name}')
class Teacher(OldboyPeople):
def __init__(self, name, age, sex, level, salary):
# python2中需要传入当前的类名、self
# super(Teacher,self).__init__(name,age,sex)
# python3中直接super()调用就可以
super().__init__(name, age, sex)
self.level = level
self.salary = salary
# def f1(self):
# print(f'hallo,{self.name}')
print(Teacher.mro())
# [<class '__main__.Teacher'>, <class '__main__.OldboyPeople'>, <class 'object'>]
tea_obj = Teacher('egon', 18, 'male', 10, 3000)
tea_obj.f1()
# 对象查找f1函数,没有;去对象的类Teacher中找,有执行,结束;
# 没有按照mro列表找到父类,去父类中查找,有执行,打印
# 你好呀!egon
print(tea_obj.__dict__)
super()案例:
class A:
def test(self):
print('from A')
super().test()
pass
class B:
def test(self):
print('from B')
class C(A,B):
pass
obj=C()
print(C.mro())
#[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]
obj.test()
#查找顺序:obj-->C-->A(打印)-->B(打印)
# from A
# from B
三、多态
1、什么是多态
同一个事物有多种形态
例:动物可以使人、猫、猪等形态:
class Animal:
pass
class People(Animal):
pass
class Dog(Animal):
pass
class Pig
pass
2、为什么要有多态
多态性指的是可以在不考虑对象具体类型的情况下而直接使用对象
例如:不管是什么动物形态,只要是动物都能发出声音
定义父类用继承的方法统一所有子类的方法:
class Animal: # 统一所有子类的方法
def say(self):
print('动物基本的发声频率。。。', end=' ')
class People(Animal):
def say(self):
super().say()
print('嘤嘤嘤嘤嘤嘤嘤')
class Dog(Animal):
def say(self):
super().say()
print('汪汪汪')
class Pig(Animal):
def say(self):
super().say()
print('哼哼哼')
obj1 = People()
obj2 = Dog()
obj3 = Pig()
obj1.say()
# 动物基本的发声频率。。。 嘤嘤嘤嘤嘤嘤嘤
obj2.say()
# 动物基本的发声频率。。。 汪汪汪
obj3.say()
# 动物基本的发声频率。。。 哼哼哼
#定义统一的接口,接收传入的动物对象
def animal_say(animal):
animal.say()
animal_say(obj1)
animal_say(obj2)
animal_say(obj3)
python中一切皆对象,内置方法中也具有多态性
如len:
print('hello'.__len__()) #5
print([1,2,3].__len__()) #3
print({'a':1,'b':2}.__len__()) #2
#自定义一个计算长度的接口
def my_len(val):
return val.__len__()
print(my_len('hello'))
print(my_len([1,2,3]))
print(my_len({'a':1,'b':2}))
#和len()方法实现的原理
len('hello')
len([1,2,3])
len({'a':1,'b':2})
但其实我们完全可以不依赖于继承,只需要制造出外观和行为相同对象,同样可以实现不考虑对象类型而使用对象,这正是Python崇尚的“鸭子类型”(duck typing):“如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子”。比起继承的方式,鸭子类型在某种程度上实现了程序的松耦合度,如下
linux中一切皆文件,看起来像文件,又能像文件那样用,那它就是文件:
class Cpu:
def read(self):
print('cpu read')
def write(self):
print('cpu write')
class Mem:
def read(self):
print('mem read')
def write(self):
print('mem write')
class Txt:
def read(self):
print('txt read')
def write(self):
print('txt write')
obj1=Cpu()
obj2=Mem()
obj3=Txt()
obj1.read()
obj1.write()
obj2.read()
obj2.write()
obj3.read()
obj3.write()
可以通过在父类引入抽象类的概念来硬性限制子类必须有某些方法名
import abc
# 指定metaclass属性将类设置为抽象类,抽象类本身只是用来约束子类的,不能被实例化
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法
def talk(self): # 抽象方法中无需实现具体的功能
pass
class Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准
def talk(self):
pass
cat=Cat() # 若子类中没有一个名为talk的方法则会抛出异常TypeError,无法实例化
四、绑定方法与非绑定方法
1、绑定方法
特殊之处在于将调用者本身当作第一个参数自动传入
—绑定给对象的方法:调用者是对象,自动传入的是对象
—绑定给类的方法:调用者是类,自动传入的是类
绑定给类的方法:
import settings
'''
文件settins内容:
IP='127.0.0.1'
PORT=3306'''
class Mysql:
def __init__(self, ip, port):
self.ip = ip
self.port = port
@classmethod # 将下面的函数装饰成绑定给类的方法
def from_conf(cls):
print(cls)
return cls(settings.IP, settings.PORT)
obj1 = Mysql('1.1.1.1', 3306)
print(obj1.ip) # 1.1.1.1
obj2 = Mysql.from_conf()
print(obj2.ip) # 127.0.0.1
print(obj2.__dict__)
# {'ip': '127.0.0.1', 'port': 3306}
2、非绑定方法(静态方法)
没有绑定给任何人:调用者可以是类、对象、没有自动传参的效果
class Mysql:
def __init__(self, ip, port):
self.nid = self.create_id()
self.ip = ip
self.port = port
@staticmethod # 将下述函数装饰成一个静态方法
def create_id():
import uuid
return uuid.uuid4()
@classmethod
def f1(cls):
pass
def f2(self):
pass
obj1 = Mysql('1.1.1.1', 3306)
# print(Mysql.create_id)
# <function Mysql.create_id at 0x000001936877A1F0>
# print(obj1.create_id)
# <function Mysql.create_id at 0x000001936877A1F0>
print(Mysql.create_id())
# 476cad69-a354-418b-a84d-596e5288de8a
print(obj1.create_id())
# f90895c7-cc72-4fd3-9d08-2bfcd8550fdc
# 绑定非绑定对比
print(Mysql.create_id)
print(Mysql.f1)
print(obj1.f2)