面向对象之元类介绍
Posted 小狗子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象之元类介绍相关的知识,希望对你有一定的参考价值。
1.元类的介绍
首先来个知识储备:
# 补充知识点exec # 全局作用域global() # 局部作用域locals() # exec(object,globals(),locals()) 三个参数 需要运行的字符串,全局作用域,局部作用域 g = { "x": 1, "y": 2 }
l = {} # 放局部作用域字典
exec("""
global x , m #声明x,m为全局 作用域
x=10 #修改
m=1
z=100 # 函数内的局部作用域
""", g, l)
print(g) #{x:10,y:2,m:1....}
print(l) #{z:100}
1.类也是对象
python中一切皆是对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例),因而我们可以将类当作一个对象去使用,同样满足第一类对象的概念,可以:
把类赋值给一个变量
把类作为函数参数进行传递
把类作为函数的返回值
在运行时动态地创建类
# 一切皆对象,对象可以怎么用?
# 1、都可以被引用,x=obj
# 2、都可以当作函数的参数传入
# 3、都可以当作函数的返回值
# 4、都可以当作容器类的元素,l=[func,time,obj,1]
比如:
class Foo:
pass
f1=Foo() #f1是通过Foo类实例化的对象
#type函数可以查看类型,也可以用来查看对象的类,二者是一样的
print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print(type(Foo)) # 输出:<type 'type'>
2.什么是元类
元类是类的类,是类的模板
元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为
元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例,Foo类是 type 类的一个实例)
type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
3.创建类的两种方式
方式一:使用class关键字
class Chinese(object): country='China' def __init__(self,name,age): self.name=name self.age=age def talk(self): print('%s is talking' %self.name)
方式二:就是手动模拟class创建类的过程):将创建类的步骤拆分开,手动去创建
# 我们用type来创建一个对象
# 定义类有三个要素1.类得名字,2,类得基类,3,类得名称空间
class_name = "Chinese" # 类得名字
class_base = (object,) #类得继承的类
class_body = '''
country='China'
def __init__(self,namem,age):
self.name=namem
self.age=age
def talk(self):
print('%s is talking' %self.name)
'''
class_dic = {} # 这个用法是用exec 把 class_body 转换成局部作用域,放在class_dic中
exec(class_body, globals(), class_dic)
print(class_dic) # 打印得到一个局部作用域
Chinese1 = type(class_name, class_base, class_dic) # 实际就是类创建过程
print(Chinese1) # 这样就会得到一个<class '__main__.Chinese'>的类
五 自定义元类控制类的行为 !!!
一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的行为,工作流程是什么)
我们定义元类如下,来控制类的创建(必须有注释,且类名称首字母必须大写)
class Mymate(type):
def __new__(cls, name, base, attr):
if "__doc__" not in attr or not attr.get("__doc__").strip():
# 判断__doc__在不在字典里,还有在字典里值不能为空 否则抛出异常
raise TypeError('必须为类指定文档注释')
if not name.istitle():
raise TypeError('首字母大写') # 判断首字母大写
return type.__new__(cls, name, base, attr)
#***********************************************
class MyMeta(type):
def __init__(self, class_name, class_base, class_body):
# 这个init方法是创建对象的时候触发 class Chinese() 时候就会触发
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')
if '__doc__' not in class_body or not class_body['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')
super().__init__(class_name,class_base,class_body)
#*************************************************
上面的两种方法那种更合适???
class People(object, metaclass=Mymate):
'''dasda'''
country = "china"
skip = "yellow"
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print("%s is talking" % self.name)
控制类实例化的行为,那么需要先储备知识__call__方法的使用
class MyMeta(type):
def __init__(self, class_name, class_base, class_body):
# 这个init方法是创建对象的时候触发 class Chinese() 时候就会触发
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')
if '__doc__' not in class_body or not class_body['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')
super().__init__(class_name, class_base, class_body)
def __call__(self, *args, **kwargs):
# 进入call方法做了几件事
# 1.生成一个实例对象
# 2.给新造的对象初始化
# 3.返回这个对象
print(self)
obj = object.__new__(self) # 造一个新的对象
print(obj)
self.__init__(obj, *args, **kwargs)
# 实例化的过程中产生的新对象调用新对象的init方法
# 这个地方其实就把实例的__dict__中添加我们设置好的属性
print("__call__", args)
print("__call__", kwargs)
print(obj)
return obj
class Chinese(object, metaclass=MyMeta):
"""..."""
country = "china"
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print("%s is talking !" % self.name)
obj = Chinese('alex', age=12) #实例化的过程调用__call__方法
obj.talk()
print(obj.__dict__)
在元类中控制自定义的类无需init方法
1.元类帮其完成创建对象,以及初始化操作;
2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
3.key作为用户自定义类产生对象的属性,且所有属性变成大写
class Mymetaclass(type):
# def __new__(cls,name,bases,attrs):
# update_attrs={}
# for k,v in attrs.items():
# if not callable(v) and not k.startswith('__'):
# update_attrs[k.upper()]=v
# else:
# update_attrs[k]=v
# return type.__new__(cls,name,bases,update_attrs)
def __call__(self, *args, **kwargs):
if args:
raise TypeError('must use keyword argument for key function')
obj = object.__new__(self) #创建对象,self为类Foo
for k,v in kwargs.items():
obj.__dict__[k.upper()]=v #这一步是直接把参数加到__dict__里面了 不用__init__方法,实际上 也是__call__调用init方法的,现在他自己实行
return obj
class Chinese(metaclass=Mymetaclass):
country='China'
tag='Legend of the Dragon' #龙的传人
def walk(self):
print('%s is walking' %self.name)
p=Chinese(name='egon',age=18,sex='male')
print(p.__dict__)
自定义元类控制类得实例化实现单例模式
单例模式后面的设计模式会讲到,这里不仔细讲解
主要是为了节约内存空间,避免多个重复实例的创建
先看下面的例子
class mysql():
__instance = None # 设置类得属性,刚开始为none
def __init__(self):
self.addr = "127.0.0.0"
self.port = 1208
@classmethod
def singleton(cls):
if not cls.__instance: #判断实例是否已经创建了,没创建则创建
obj = cls()
# 这一步实际做的是实例化一个对象 用了类方法、把类传进去 实例化一个类得对象obj,赋值给__instance 这个类变量
# 赋值给cls.__instance 这个变量
cls.__instance = obj # 创建之赋值给__instance
return cls.__instance #返回生成的实例
# 一下的做法其实是生成两个mysql 实例 可是他们都是一样的 但是占用内存 所以引入单例模式 一样的就不创建了,用以前的就好
# m1=Mysql()
# m2=Mysql()
#
# print(id(m1))
# print(id(m2))
#使用类方法的单例模式创建数据引擎,避免内存浪费
m1 = Mysql.singleton()
m2 = Mysql.singleton()
print(id(m1))
print(id(m2))
以上是关于面向对象之元类介绍的主要内容,如果未能解决你的问题,请参考以下文章