1 Mixins机制
2 元类简介
3 产生类的两种方式
4 元类的进阶操作
5 回顾面向对象
1 Mixins机制
class Vehicle: # 交通工具
pass
class FlyMinix():
def fly(self):
'''
飞行功能相应的代码
'''
print("I am flying")
"""
1. 主类:就是大部分都是主要的功能
2. 辅类:就是一些辅助的功能
3. 辅类的类名也有讲究:一般以mixin、able、ible等结尾
4. 继承的位置也有讲究
"""
class CivilAircraft(FlyMinix, Vehicle): # 民航飞机
pass
class Helicopter(FlyMinix, Vehicle): # 直升飞机
pass
class Car(Vehicle): # 汽车并不会飞,但按照上述继承关系,汽车也能飞了
pass
import socketserver
2 元类简介
'''
什么是元类?
即产生类的类
'''
"""type其实查看的是对象是有哪个类产生的!"""
class MyClass(object):
pass
obj = MyClass()
# <class '__main__.MyClass'>
print(type(obj)) # 可以查看出obj对象是有哪个类产生的
"""你说我打印type(MyClass) 它的结果应该是什么呢?"""
# class type(object):
# print(type(MyClass)) # <class 'type'> 我就可以认为MyClass类就是有type类产生的
class Student:
pass
# print(type(Student)) # <class 'type'>
class C1():
pass
print(type(C1)) # <class 'type'>
"""得出结论:我们之前使用class造出来的类都是由type类造出来的"""
"""我们说默认情况下,所有的类的元类就是type"""
3 产生类的两种方式
'''
1.class 类名():
pass
2.由于所有类都是有type类造出来的,所以我们也可以使用type类造出来一个新类
type(object_or_name, bases, dict)
这三个条件就是造出来类的三个要素.
type("类名", "父类", "类的名称空间")
3.为什么要学习元类?
'''
"""
元类是造出类的类,所以,我们可以对类进行高度的定制化.
举个例子:
我让你写出来的类名必须是首字母大写?
思考:我们的定制化代码在哪里写呢?
类是如何产生的?类是由元类产生的type类,如果想改类的行为,应该在元类里的__init__方法中改.
对象是如何产生的? 是执行了产生这个对象的类内部的__init__方法
推导:已知推出来的未知.
注意:
想改父类的__init__方法, 但是又不能修改源代码,所以我们可以写出来一个子类来继承父类type,
在子类里面写代码,然后在从新执行一些父类的__init__方法.
"""
"""元类不能直接继承"""
class MyTypeClass(type):
def __init__(self, cls_name, cls_bases=None, cls_dict=None):
print(cls_name, cls_bases, cls_dict) # C1 () {'__module__': '__main__', '__qualname__': 'C1'}
# cls_name:它就是类名
# 限制类名必须是首字母大写
if not cls_name.istitle():
raise Exception("类名必须是首字母大写")
super().__init__(cls_name, cls_bases=None, cls_dict=None)
class C1(metaclass=MyTypeClass):
school = 'Sh'
class a(metaclass=MyTypeClass):
pass
4 元类的进阶操作
'''
__call__方法
对象加括号会自动调用类的__call__方法,并且,__call__方法里面返回什么,那么,对象加括号的位置就是什么
推导:类名()会怎么样?
"""类名(),应该是会执行产生类的类中的__call__方法,而这个类恰好是元类type"""
'''
class MyClass(type):
def __call__(self, *args, **kwargs):
print("__call__ 执行了")
print(args, kwargs)
"""限制传参的方式必须是关键字传参,只需要args是空就行"""
if args:
raise Exception("传参必须是关键字传参")
super(MyClass, self).__call__(*args, **kwargs)
class C1(metaclass=MyClass):
def __init__(self, name):
print("__init__ 执行了")
# self.name = name
"""看C1类里面的__init__方法和元类里面的__call__方法的执行顺序"""
# 得出结论,其实在执行类的__init__之前,先执行了__call__方法
obj = C1(name='kevin') # 都是位置传参,强制要求必须是关键字传参
5 回顾面向对象
面向过程
面向对象
"""对象就是盛放数据属性和方法的容器"""
类的定义和对象的产生
class Student():
school = 'SH'
def index(self):
pass
"""
定义类发生了什么事情:
1. 一旦被定义,会立马执行类体代码
2. 会产生类的名称空间,然后,把类中的名字丢到类的名称空间去,去就是放到字典里去了
3. 把类的名称空间绑定给了__dict__属性, 类名.__dict__
"""
如何产生对象
实例化,调用类,类名()
stu = Student() 默认实例化出来的对象是一个空对象,其实就是一个空字典
stu1 = Student()
print(stu.__dict__) # {}
定制对象自己独有的属性
方式1:
stu.__dict__['name'] = 'kevin'
stu.__dict__['age'] = '19'
stu.__dict__['gender'] = 'female'
方式2:
stu.name = 'kevin'
stu.age= '18'
stu.gender= 'male'
方式3:
def init(stu_obj, name, age, gender):
stu_obj.name = name
stu_obj.age = age
stu_obj.gender = gender
init("kevin", 19, 'male')
init("kevin1", 29, 'female')
方式4:
class Student():
school = 'SH'
def __init__(self, name, age, gender):
self.name = name
self.age = age
self.gender = gender
def index(self):
pass
stu = Student("kevin", 19, 'male') # Student(stu, "kevin", 19, 'male')
print(stu.name)
绑定方法和非绑定方法
绑定方法:
1. 绑定给对象的
由对象来调用,特殊之处是会把对象自己当成第一个参数传给方法的第一个形参:self
类也能来调用,只不过有几个参数就要传几个参数,一个不能少,一个也不能多,包括self
2. 绑定给类的
先给方法的头上加一个装饰器:@classmethod, 方法的第一个形参就改成了cls
由类来调用,特殊之处是把类名当成一个参数传给方法的第一个形参:cls
非绑定方法:
谁都不绑定
先在方法的头上加一个装饰器:@staticmethod,加了这个装饰器之后,方法的self、cls都可以不要
对象和类都可以来调用,就当成普通函数只有即可
隐藏属性
1、如何隐藏:在名字的前面加上__双下划线
2、隐藏之后,名字会发生变形,_类名__属性名
3、隐藏之后,在类的外部不能直接使用了,若非要使用也能用
4、隐藏属性就是为了对外部进行更好的限制,在类的内部定义一个接口,然后返回隐藏的属性
property装饰器
把方法伪装成属性来使用
给方法名加装饰器
1. property装饰器
2. 方法名.setter
3. 方法名.deleter
另外一种方式:
xxx = property('', '', '')
'''这种方式是有顺序要求的'''
例如:
country = property(get_country, set_country, del_country)
继承:
1. 继承就是新建类的一种方式,新建出来的类称之为子类,被继承的类称为是父类
2. 子类可以遗传父类的属性和方法
3. 继承解决的是类与类之间的代码冗余问题
4. 继承的分类:
1. 经典类
2. 新式类:就是继承object类的类
"""在python3中,都是新式类"""
继承下的属性查找:
单继承下的
先从对象自己的名称空间查找,再去类中查找,最后在去继承的父类中查找
多继承下的
菱形查找和非菱形查找
非菱形查找就是按照一个分支一个分支的查找
菱形查找
经典类:深度优先查询,就是从第一个分支直接找到顶点
新式类:广度优先查询,就是从最后一个分支直接找到定点
super和mro的使用
super的使用是依赖继承的,它的查找顺序是遵循mro列表
mro列表是一种c3算法的结果,每个类的mro列表不一样,但是需要从起始类开始
多态与多态性
一种事物的多种形态
抽象类:在父类中限制子类的行为,而不是异常父类的功能
"""方式1"""
import abc
class Animal(metaclass=abc.ABCMeta):
@abc.abstractmethod
def speak(self):
pass
class Dog(Animal):
def speak(self):
pass
"""方式2"""
class Animal():
def speak(self):
raise Exception("")
class Dog(Animal):
def speak(self):
pass
class Dog():
def speak(self):
pass
class Pig():
def speak(self):
pass
class People():
def speak(self):
pass
"""鸭子类型:更多的关注的是对象的行为,而不是对象的类型"""
多态性:在不考虑对象的类型下,直接调用对象的方法
举个多态的例子:len
组合:
一个对象拥有一个属性,该属性的值是另外一个对象
魔术方法
...
反射
...
异常
...
元类
...