目录
1.元类
1.1 类创建过程
1.2 元类控制类的创建
1.3 元类控制类的创建过程案例
1.4 自定义元类控制类的实例化
1.5 元类控制类的实例化练习
1.6 加入元类后属性查找顺序
1.7 object和type的关系
1.8 元类中控制把自定义类的数据属性都变成大写
1.9 元类中控制自定义的类无需__init__方法
2.单例
2.1 模块导入
2.2 使用类装饰器
2.3 使用类的绑定方法
2.4 使用`__new__`方法
2.5 使用元类实现单例
3.总结
面向对象初级部分
面向对象高级部分
1.元类
'''
元类:
1 元类介绍:黑魔法,产生类的类---》称之为元类
2 类也是对象---》对象就是由类产生---》元类--》默认情况下就是type
3 type(对象)---》查看对象类型
4 type(类)----》type
5 一个类是由 类名字 类的父类元祖 类的名称空间 三个部分组成
1 python中一切皆对象,类 People,Animal...,本质也是对象
2 既然是对象,就有一个类来实例化得到它(类)
3 实例化得到类的类,就是元类
4 默认情况下,元类是type这个类(包括自己)
-默认情况下,所有类都是由type这个类实例化得到的
'''
# People这个类是由元类实例化得到的,不需要咱么手动操作
# 手动创建类,而不是使用class来创建类
class People():
def __init__(self,name):
self.name=name
p=People('mzk')
# type 内置函数
# type--->类--->类的实例化
print(type(p)) # 看p的类是谁,看p的类型
print(type(People)) # People这个类,也是一个对象,它的类是type
print(type(type))
### 默认情况下,所有类都是由type这个类实例化得到的
1.1 类创建过程
'''
所有的类,都是由元类创建产生
我们原来,都是使用class创建的
复原class的本质,使用type来创建类
'''
# 1 内置函数exec(储备)
ss = '''
a=10
b=100
di={'name':'lqz'}
def test():
print('我是test')
'''
# 第一个参数是要执行的字符串,
# 第二个参数是全局名称空间(所有的内置函数)
# 第三个参数是局部名称空间(字符串产生的函数,变量)
dic1 = {}
dic2 = {}
exec(ss, dic1, dic2)
# print(dic2)
# print(dic2['a']) # 从局部名称空间中取出变量a
# dic2['test']() ## 从局部名称空间中取出函数test,并且执行
print(dic1)
# 2.1 类的创建过程(类对象实例化的过程)
# 原来类创建的过程
# class People(object):
# pass
# 2.2 使用type来创建类
# People=type()
# 需要类名
class_name = 'People'
# 需要父类(基类)
class_bases = (object,)
# 类体(类的名称空间)
class_body = '''
name='lqz'
age=19
def print_name(self):
print(self.name)
'''
class_namespace = {}
exec(class_body, {}, class_namespace)
print(class_namespace)
# 创建类
People = type(class_name, class_bases, class_namespace)
# 相当于
class People(object, ):
name = 'lqz'
age = 19
def print_name(self):
print(self.name)
print(People)
p = People()
print(p)
p.name = 'egon'
p.print_name()
1.2 元类控制类的创建
'''
要控制类的创建过程 只要找到类所属的类 中的__init__即可
一个对象的创建,调用对象的类的元类---》__call__--->调用了类的__new__和 __init__
默认所有的类都是由type创建出来
自己定义一个元类,让类是由我们定义的元类创建出来的
-写了一个类,继承了type---》自定义的元类
class People(metaclass=自定义的元类)
pass
People=自定义的元类('类名',基类元组,类的名称空间)
会触发[自定义元类]的__init__,在这里面写逻辑
控制类的创建过程
1.创建一个元类 (需要继承type)
2.覆盖__init__方法 该方法 会将新建的类对象 类名 父类们 名称空间 都传进来 ,
可以利用这些信息在做处理
3.对于需要被控制的类 需要指定metaclass 为上面的元类
控制类实例化对象的过程
1.创建一个元类 (需要继承type)
2.覆盖__call__方法 会将 正在实例化对象的类 调用类是传入的参数 都传进来
3.在__call__方法中 必须要先编写模板代码
3.1创建空对象
3.2调用类的__init__方法来初始化这个空对象
3.3返回该对象
4.加入你需要控制的逻辑
类的三个组成部分
类名 父类们 名称空间
元类 -> 实例化产生 -> 类 -> 实例化产生 -> 对象
'''
class MyType(type): # 如果是元类,必须继承type
def __init__(self, class_name, class_bases, class_namespace):
# print('类名是:',class_name)
# print('基类是:',class_bases)
# print('名称空间是:',class_namespace)
# 创建类
# 调用父类的__init__完成类的初始化
print(class_name, '开始初始化了')
super().__init__(class_name, class_bases, class_namespace)
print(class_name, '初始化完成了')
# 等同于
# MyType('People' ,(object,) ,class_namespace)
# 你写 class People 等同于:MyType('People' ,(object,) ,class_namespace)
# 触发MyType的__init__--->调用了父类的初始化:super().__init__('People' ,(object,) ,class_namespace)
class People(metaclass=MyType): # 默认用type,要用自己的元类
name = 'lqz'
def print_name(self):
print(self.name)
p = People()
p.print_name()
1.3 元类控制类的创建过程案例
# 控制类首字母必须大写
class MyType(type): # 元类,必须继承type
def __init__(self, name, class_bases, dic):
# 判断name如果不是以大写字母开头,就抛出异常
if not name[0].isupper():
raise Exception('首字母必须为大写')
# 使用父类的__init__完成初始化
super().__init__(name, class_bases, dic)
class People(metaclass=MyType): # 指定这个类使用我的元类创建
def __init__(self,name):
self.name=name
p=People('mzk')
print(p.name)
# 我们定义的类,必须有注释,否则创建不成功
class MyType(type): # 元类,必须继承type
def __init__(self, name, class_bases, dic):
doc=dic.get('__doc__')
# print(doc)
if doc and doc.strip():
super().__init__(name, class_bases, dic)
else:
raise Exception('必须有注释')
class People(metaclass=MyType): # 指定这个类使用我的元类创建
'''
注释
'''
def __init__(self,name):
self.name=name
p=People('mzk')
print(p.name)
1.4 自定义元类控制类的实例化
'''
元类的__call__---->类的__new__和类的__init__
class MyType(type):
def __call__(self, *args, **kwargs):
obj=self.__new__(self)
if isinstance(obj,self):
obj.__init__(*args, **kwargs)
return obj
else:
return obj
'''
# 会出递归的方式,错误方式
class MyType(type):
def __call__(self, *args, **kwargs):
# print(args)
# print(kwargs)
# # 创造对象(new出来,init初始化)
# # 创建空对象
# print(self) # self是People 这个类
# People(*args, **kwargs) 等同于,所以出递归
self(*args, **kwargs) # 类加括号,实例化得到对象, 触发元类的__call__,递归了
class People(metaclass=MyType):
def __init__(self, name):
self.name = name
# 对象加括号,会触发类中的__call__方法
# 类加括号,会触发元类的__call__方法
p = People('mzk') # 会触发元类MyType的__call__方法的执行
print(p)
# 正确方式
class MyType(type):
def __call__(self, *args, **kwargs):
# self是People,Animal等,所有被MyType控制的类
# 创建出空People对象
# obj=People.__new__(People)
# 空对象
obj = self.__new__(self) # obj是People的对象
# 调用__init__,完成对象的初始化
obj.__init__(*args, **kwargs)
return obj
class People(metaclass=MyType):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
# class Animal(metaclass=MyType):
# pass
# 对象加括号,会触发类中的__call__方法
# 类加括号,会触发元类的__call__方法
p = People('mzk') # 会触发元类MyType的__call__方法的执行
print(p)
1.5 元类控制类的实例化练习
# 原始操作
class People():
def __init__(self,name,age):
self.__name=name
self.__age=age
p=People('mzk',19)
# 使用元类来实现(普通版本)
class MyType(type):
def __call__(self, *args, **kwargs):
# 创建空对象
obj = self.__new__(self)
# 完成初始化
# obj.__dict__['_People__name']='lqz'
# obj.__dict__['_People__age']=19
for key, value in kwargs.items():
obj.__dict__['_%s__%s' % (self.__name__, key)] = value
return obj
class People(metaclass=MyType):
def print_name(self):
print('我的名字叫', self.__name)
p = People(name='mzk', age=19)
# p.print_name()
print(p.__dict__)
## 使用元类来实现(高级版本)
class MyType(type):
def __call__(self, *args, **kwargs):
obj=self.__new__(self)
obj.__init__(*args, **kwargs)
print(obj.__dict__)
# for key,value in obj.__dict__.items(): ### 出bug,循环字典的时候,往字典中添加之,导致循环次数不确定
# obj.__dict__['_%s__%s'%(self.__name__,key)]=value
# dic={}
# for key,value in obj.__dict__.items():
# dic['_%s__%s'%(self.__name__,key)]=value
#
# obj.__dict__=dic
obj.__dict__={'_%s__%s'%(self.__name__,key):value for key,value in obj.__dict__.items()}
return obj
class People(metaclass=MyType):
def __init__(self,name,age):
self.name=name
self.age=age
p = People(name='lqz',age= 19)
print(p.__dict__)
# print(p.__name)
print(p._People__name)
1.6 加入元类后属性查找顺序
# 查找顺序:
'''
先对象层:OldoyTeacher->Foo->Bar->object
然后元类层:Mymeta->type
对象---->类中--->父类(mro列表) |||---->自己定义的元类--->type
对象自己---》类--》父类(mro列表)--》自己定义的元类---->type---> 报错了
'''
1.7 object和type的关系
## 最顶层的关系
'''
# 最顶层
object
type
# 内置的
list
dict
int
str
# 自定义类
People
Animal
'''
'''
所有类都继承object,包括type类,但是不包括object
所有类的元类都是type,包括type自己,包括object
object和type的关系
-type是元类,是所有类的类,包括type,包括object
-object是所有类的父类,包括type,不包括object
'''
# print(list.__bases__)
print(object.__bases__)
# print(type(object))
type
1.8 元类中控制把自定义类的数据属性都变成大写
# callable() :能加括号调用,返回true,不能被调用,就返回false
a=10
def test():
print('xxx')
class People:
@property
def test(self):
return 10
p=People()
print(callable(p.test))
# 元类变大写
class MyType(type):
def __init__(self, name, base_class, dic):
# 到此,People这个类,已经new出来了,后续要做一些初始化的工作,再改__dict__不允许改
# 在类没创建之前,把dic,改成咱们想要的样子
print(self.__dict__)
# print(type(self.__dict__))
self.__dict__ = {}
super().__init__(name, base_class, dic) # 不调用,也没问题
def __new__(cls, name, base_class, dic):
print(dic)
new_dic = {}
for key, value in dic.items():
if key.startswith('__') or callable(value):
new_dic[key] = value
else:
new_dic[key.upper()] = value
print(new_dic)
return super().__new__(cls, name, base_class, new_dic)
class People(metaclass=MyType):
name = 'mzk '
age = 19
def print_info(self):
print('名字是%s,年龄是%s' % (self.name, self.age))
print(People)
print(People.__dict__)
class Animal(metaclass=MyType):
type_s = 'Dog'
aa = 'aa'
print(Animal.__dict__)
class Animal:
def test(self):
print('test')
class People(Animal):
def test(self):
# super().test()
print('test')
print('Peope 的text')
1.9 元类中控制自定义的类无需init方法
class MyType(type):
def __call__(self, *args, **kwargs):
if args:
raise Exception('必须以关键字传参')
# 生成一个空对象
obj = self.__new__(self)
# 调用对象__init__完成初始化(类中没有__init__,手动完成)
for key, value in kwargs.items():
# setattr(obj,key,value) ## 方法一
obj.__dict__[key] = value # 方法二
return obj
class People(metaclass=MyType):
# def __init__(self,name,age):
# self.name=name
# self.age=age
pass
# p = People(name='mzk', age=19)
# p = People(name='mzk', age=19, sex='男')
p = People('mzk')
print(p.__dict__)
2.单例模式
2.1 模块导入
# 第一种:使用模块导入
# 不管导入多少次,setting对象都是同一个对象,节约了内存
from a import setting
from a import setting as s1
from a import setting as s2
print(setting is s1)
print(setting is s2)
# 错误的演示
from a import Setting
ss1=Setting()
ss2=Setting()
print(ss1 is ss2)
2.2 使用类装饰器
# 第二种使用类装饰器实现
def Singleton(cls):
instance = None
def inner():
# 使用一个变量,存对象,以后,每次都返回这个对象
# 这个对象如果没有,需要先造出来
nonlocal instance # 声明一下,内部使用的instance,是外层的instance
if not instance:
instance=cls() # 咱么认为是赋值,机器认为你是在定义
return instance
return inner
@Singleton # Setting=Singleton(Setting)----->Setting就是inner内层函数
class Setting:
HOST = '127.0.0.1'
PORT = 22
USER = 'root'
PASSWORD = '123'
s1=Setting() # inner()
s2=Setting()
s3=Setting()
s4=Setting()
s5=Setting()
print(s1 is s2)
print(s1 is s5)
2.3 使用类的绑定方法
# 第三种方式:类的绑定方法来实现
class Setting:
__instance = None
HOST = '127.0.0.1'
PORT = 22
USER = 'root'
PASSWORD = '123'
@classmethod
def get_singleton_obj(cls):
# 使用一个变量,存储对象,如果对象存在,直接返回,如果不存在,创建对象
if not cls.__instance:
cls.__instance=cls() # 类实例化得到对象,赋值给类.__instance
return cls.__instance
s1=Setting.get_singleton_obj()
s2=Setting.get_singleton_obj()
# 这种方案不能再使用Setting() 实例化得到对象了
## 错误的示范
# s3=Setting()
# s4=Setting()
# print(s3 is s4)
# print(s1 is s4)
print(s1 is s2)
2.4 使用__new__
方法
# 第四种:基于new方法实现
class Setting:
__instance=None
HOST = '127.0.0.1'
PORT = 22
USER = 'root'
PASSWORD = '123'
def __new__(cls, *args, **kwargs):
# obj=super().__new__(cls) # 使用父类对象生成
# obj=object.__new__(cls) # 直接使用object生成
if not cls.__instance:
cls.__instance=super().__new__(cls)
return cls.__instance
s1=Setting() # 会触发类的__new__,只要__new__返回同一个对象
s2=Setting()
print(s1 is s2)
2.5 使用元类实现单例
# 第五种:通过元类实现
class MyType(type):
__instance=None
def __call__(self, *args, **kwargs):
# self 就是 Setting这个类
# obj=self.__new__(self)
# obj.__init__(*args, **kwargs)
if not self.__instance:
self.__instance=self.__new__(self)
return self.__instance
class Setting(metaclass=MyType):
HOST = '127.0.0.1'
PORT = 22
USER = 'root'
PASSWORD = '123'
s1=Setting() # 触发元类的 __call__
s2=Setting()
print(s1 is s2)
总结
面向对象初级部分
1 OOP 编程
2 面向对象编程,区别于面向过程编程
-最大核心:有对象,有属性,有方法
3 类与对象
class People:
pass
p=People()
p.属性
p.方法()
4 定制对象的独有特征
# 类和对象都有自己的名称空间
# 对象.属性--->优先使用自己名称空间的属性--->自己没有使用类的
# 对象.属性=value---->不会修改掉类名称空间的值--->只改自己的
5 对象属性查找属性
# 先从对象自己找,找不到去类中找,再找不到报错
# 类属性的更改 类名.属性=变更 不会影响对象自己的名称空间
6 对象的绑定方法
# 方法和函数的区别?
-方法会自动传值,方法是绑定给某个对象的
-函数有几个值就传几个值
-注意点:对象来调用是方法,类来调用是函数
# 类内部定义,没有被装饰器修饰的方法,都是对象的绑定方法
7 类和类型
# type(对象)---->类
# 如何去查看源码:typpe(对象)
from xx import 类
8 有对象和没有对象的区别
以后一般情况尽量使用类来写
9 面向对象的继承
-少些代码
-class People(Animal):
pass
-多继承
-新式类
-经典类
-继承后的属性查找顺序
-对象---》类---》父类(从左到右)
10 类的派生
-有了继承关系以后,在子类中新的属性和方法,称为派生
-子类重写父类的方法,功能增加,也叫派生
11 面向对象的组合
-对象中套对象
-对象中的对象其实是一个引用
12 菱形问题(必须出现菱形,最顶层objct不算)
-新式类:广度优先
-经典类:深度优先
-继承关系的mro列表
-以后看源码的时候
self.方法(),不要盲目的按照ctrl键点,需要从根上往上找
13 super()
-特殊对象----》父类对象(严格按照mro列表查找)
-类名.方法() 指名道姓的使用,不会乱
-super()调用方法不需要传self
-类名来调用方法需要传self
14 多态和多态性
-多态
-同一类食物的多种形态
-有一个父类(Animal)
-有一堆子类,继承父类(People,Cat,Dog)
-子类实例化得到对象,这就叫多态
-多态性
-不考虑对象具体类型情况下,使用对象
-people,cat,dog
-run(对象)
-鸭子类型
-走路像鸭子,看起来像鸭子,对象就是鸭子
-代码体现
-abc模块限制子类必须重写父类的方法
-抛异常的方式限制子类必须重写父类的方法
15 类的封装
-把属性和方法包装到类内部
-把属性或方法隐藏,只让属性跟方法在内部使用,只对外暴露简单接口(方法)
-属性或方法前加 __ 也会看到只加 _
-外部也可以使用到隐藏属性和方法 _类名__属性名
-内部正常使用
16 properyt
-把方法包装成数据属性
-其他使用
class Goods:
@property # 相当于@price.getter
def price(self):
print('@property')
@price.setter
def price(self, value):
print('@price.setter')
@price.deleter
def price(self):
print('@price.deleter')
17 绑定方法和非绑定方法
-没有装饰器修饰的:绑定给对象,对象来调用,自动传入对象,类来调用就是普通含税
-用classmethod修饰的:绑定给类,类来调用,自动把类传入,对象也可以来调用,把对象转成类,传入
-用staticmethod修饰的:不绑定给任何人,谁都可以来调用,有几个值装饰器
18 type __new__---->使用staticmethod修饰了---》作者之所以这样写的目的
让我们看到__new__以后,就知道它是个静态方法,不绑定给任何人
# 补充:
以后只要是python项目,根路径下都要有一个requirements.txt,文本文件
记录这个项目使用了哪些第三方模块,版本是多少
面向对象高级部分
1 isinstance和issubclass
-isinstance判断一个对象是不是一个类的对象(继承关系也能判断)
-issubclass判断一个类是不是另一个类的子类(继承关系也能判断)
2 反射
setattr:向对象中设置属性或方法(向类设置)
getattr:通过对象获取属性或方法
delattr:删除对象中的属性或方法
hasattr:判断属性或方法是否再对象中
# 反射模块
__import__ # 普通反射模块
__import__("lib.text.commons", fromlist=True) # 带点的反射
# 反射模块二
import importlib
a=importlib.import_module('a')
print(a.setting)
3 . 拦截方法
-__setattr__:对象.属性=值 触发它
-__getattr__:对象.属性 属性不存在触发
-__delattr__:del 对象.属性 触发它
-__getattribute__ # 它会先于__getattr__执行,如果重写了它, __getattr__就不会执行
4 item系列
-__setitem__: 对象['key']=value 触发它执行
-__getitem__: 对象['key'] 触发它执行
-__delitem__: del 对象['key'] 触发它执行
5 __format__
-format(对象) ---》触发类中__format__的执行
-'{:n-m}'.format(对象) ---》触发类中__format__的执行
6 __del__
del 对象 触发它指向
7 __slots__
-类属性:
-限制对象属性只能是__slots__中的
-节省内存(属性比较少,对象很多)
8 __all__
-from xx import * 导入只会导入__all__中定义的属性
9 __doc__
-类的注释
-类名.__doc__ # 打印出注释
-它不能继承
10 __call__
-对象()---->触发该对象所属类的 __call__
11 __init__和__new__
-__new__创建一个空对象
-__init__完成初始化
-new是再init之前执行
-最本质是元类中的__call__内部调用了new和init
12 __str__和__repr__
-print(对象) 会触发 __str__的执行,如果没有再触发__repr__的执行
-在命令窗口中,写对象 触发__repr__的执行
13 迭代器协议
-写一个对象,支持for循环来循环它
-重写__iter__和__next__
14 __module__和__class__
-__module__看对象属于哪个模块
对象.__module__
-__class__看对象属于哪个类
对象.__class__
15 上下文管理协议
-with 对象 as f: 触发对象的 __enter__执行,返回什么,f就是什么
-脱离了with,就会触发__exit__,异常处理和资源清理
-__enter__
-__exit__
16 描述符
-新式类,重写了 __get__,__set__,__delete__,就是描述符
-用来代理类属性
-数据描述符和非数据描述符
-通过描述符限制对象的属性类型
-类装饰器(有参)
-类装饰器+描述符实现限制对象的属性类型
-property,classmethod,staticmethod底层原理,自定制
17 元类
-类的类,默认是type
-重写元类,必须继承type
-class People(metaclass=自己写的)
-__init__
-__call__
-__new__
-通过元类控制类的创建
-通过元类控制对象的创建
18 单例模式:
-全局只有一个对象
-5种方式