Py003-01-13元类

选学内容(基本用不到)

元类介绍

储备知识 exec

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
参数1 :字符串形式的命令
参数2 :全局作用域(字典形式)如果不指定默认是globals()
参数3 :局部作用域(字典形式)如果不指定默认是locals()

g = {
"x":1,
"y":2
}

l = {}

exec('''
global x,m
x = 10
m = 100

z = 3
''',g,l)

print(g) # 全局作用域 x的值修改为 10 新增 m = 100
print(l) # 局部作用域里 l 新增了 z:3

一切皆对象,对象可以怎么用?

  1. 都可以被引用,x=obj
  2. 都可以当作函数的参数传入
  3. 都可以当作函数的返回值
  4. 都可以当作容器类的元素,l=[func,time,obj,1]

类也是对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Foo():
pass

obj = Foo()
print(type(obj))
print(type(Foo))

# <class '__main__.Foo'>
# <class 'type'>

class Bar():
pass

print(type(Bar)) # <class 'type'>

产生类的类称之为元类,默认所有用class定义的类,它们的元类是type

定义类的两种方式

  • class关键字
  • type元类产生
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# class方式
class Chinese: #Chinese=type(...)
country='China'

def __init__(self,namem,age):
self.name=namem
self.age=age

def talk(self):
print('%s is talking' %self.name)

# type方式
# 定义类的三要素:类名,类的基类们,类的名称空间
class_name='Chinese'
class_bases=(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,globals(),class_dic)

# 定义类的三要素:类名,类的基类们,类的名称空间
Chinese1=type(class_name,class_bases,class_dic)

自定义元类控制类的创建

  • 类名必须大写
  • 必须有文档注释
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')

if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')

super(Mymeta,self).__init__(class_name,class_bases,class_dic)

class Chinese(object,metaclass=Mymeta):
'''
中文人的类
'''
country='China'

def __init__(self,namem,age):
self.name=namem
self.age=age

def talk(self):
print('%s is talking' %self.name)

自定义元类控制类的实例化行为

知识储备call方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Foo():
pass

obj = Foo()
# 请问obj能当作函数调用吗?
obj(1,2,3) # 报错
# --------------------------------------
# 需要提供call方法
# 再看

class Foo2():
def __call__(self,*args,**kwargs):
print('===>')
pass

obj2= Foo2()
obj2(1,2,3) # 不报错

切入正题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')

if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')

super(Mymeta,self).__init__(class_name,class_bases,class_dic)

def __call__(self, *args, **kwargs): #obj=Chinese('hjx',age=18)
# print(self) #self=Chinese
# print(args) #args=('hjx',)
# print(kwargs) #kwargs={'age': 18}

#第一件事:先造一个空对象obj
obj=object.__new__(self)
#第二件事:初始化obj
self.__init__(obj,*args,**kwargs)
#第三件事:返回obj
return obj

class Chinese(object,metaclass=Mymeta):
'''
中文人的类
'''
country='China'

def __init__(self,namem,age):
self.name=namem
self.age=age

def talk(self):
print('%s is talking' %self.name)




obj=Chinese('egon',age=18) #Chinese.__call__(Chinese,'hjx',18)

print(obj.__dict__)

元类控制类的实例化行为

单例模式

实现方式一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class MySQL:
__instance=None #__instance=obj1

def __init__(self):
self.host='127.0.0.1'
self.port=3306

@classmethod
def singleton(cls):
if not cls.__instance:
obj=cls()
cls.__instance=obj
return cls.__instance


def conn(self):
pass

def execute(self):
pass


# obj1=MySQL()
# obj2=MySQL()
# obj3=MySQL()
#
# print(obj1)
# print(obj2)
# print(obj3)

obj1=MySQL.singleton()
obj2=MySQL.singleton()
obj3=MySQL.singleton()

print(obj1 is obj3)

实现方式二:元类的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Mymeta(type):
def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle():
raise TypeError('类名的首字母必须大写')

if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError('必须有注释,且注释不能为空')

super(Mymeta,self).__init__(class_name,class_bases,class_dic)
self.__instance=None

def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=18)
if not self.__instance:
obj=object.__new__(self)
self.__init__(obj)
self.__instance=obj

return self.__instance



class Mysql(object,metaclass=Mymeta):
'''
mysql xxx
'''
def __init__(self):
self.host='127.0.0.1'
self.port=3306

def conn(self):
pass

def execute(self):
pass



obj1=Mysql()
obj2=Mysql()
obj3=Mysql()

print(obj1 is obj2 is obj3)