元类
简介
- 类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样;当你使用class关键字时,Python解释器自动创建这个对象
- 元类就是用来创建类的“东西”;元类就是类的类
- Python中所有的东西,注意,我是指所有的东西——都是对象。这包括整数、字符串、函数以及类;它们全部都是对象,而且它们都是从一个类创建而来,这个类就是type
使用type()函数查看类型信息
class Person:
def eat(self):
print('eat')
p = Person()
print(type(Person))
print(type(p))
print(type(p.eat))
输出信息
<class 'type'>
<class '__main__.Person'>
<class 'method'>
动态创建类
def choose_class(name):
if name == 'foo':
class Foo:
def print_info(self):
print('Foo info')
return Foo
elif name == 'bar':
class Bar:
def print_info(self):
print('Bar info')
return Bar
MyClass = choose_class('foo')
MyClass().print_info()
输出信息
Foo info
使用type创建类
- type() 函数可以查看对象类型
- type() 还可以创建新的类型
class Base:
pass
def normal_method(self):
print('这是一个普通方法')
@classmethod
def class_method(cls):
print('这是一个类方法')
@staticmethod
def static_method():
print('这是一个静态方法')
# 第一个参数是字符串:类名
# 第二个参数是元组:继承关系
# 第三个参数是字典:属性和方法
Test = type('Test', (Base,),
{'attr01': True, 'normal_method': normal_method, 'class_method': class_method,
'static_method': static_method})
print(dir(Test))
==使用dir() 函数查看对象所有属性和方法==
输出信息
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'attr01', 'class_method', 'normal_method', 'static_method']
创建一个对象测试
test = Test()
print(test.attr01)
test.normal_method()
Test.class_method()
Test.static_method()
输出信息
True
这是一个普通方法
这是一个类方法
这是一个静态方法
metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass 先定义metaclass,就可以创建类,最后创建实例。 metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”
使用函数当做元类
# 遍历属性字典,把不是__开头的属性名字变为大写
def upper_attr(name, bases, attrs):
new_attrs = {}
for key, value in attrs.items():
if not key.startswith("__"):
new_attrs[key.upper()] = value
# 调用type来创建一个类
return type(name, bases, new_attrs)
class Foo(metaclass=upper_attr): # 使用metaclass
bar = '属性描述'
f = Foo()
print(f.BAR)
print(dir(f))
输出信息
属性描述
['BAR', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
使用class来当做元类
class MyMetaClass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: print('修改后数据', (value + 1))
return type.__new__(cls, name, bases, attrs)
class Foo(metaclass=MyMetaClass):
pass
f = Foo()
f.add(1)
print(dir(f))
修改后数据 2
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'add']
动态添加方法MethodType
将方法绑定到对象上
给一个实例绑定的方法,对另一个实例是不起作用的
from types import MethodType
class Person:
pass
def print_name(self):
print('name:%s' % self.name)
p1 = Person()
p2 = Person()
p1.print_name = MethodType(print_name, p1)
p1.name = 'Tom'
p2.name = 'Jake'
p1.print_name() # name:Tom
# p2.print_name() # 异常报错AttributeError
将方法绑定在类上
为了给所有实例都绑定方法,可以给class绑定方法
from types import MethodType
class Person:
pass
def print_name(self):
print('name:%s' % self.name)
Person.print_name = print_name
p1 = Person()
p2 = Person()
p1.name = 'Tom'
p2.name = 'Jim'
p1.print_name()
p2.print_name()
输出信息
name:Tom
name:Jim
使用__slots__
- Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性
- 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
class Person:
__slots__ = ('name', 'age')
p = Person()
p.name = 'Tom'
p.age = 20
print(p.name, p.age)
# p.address = 'beijing' # AttributeError异常
输出信息
Tom 20
枚举类
- 定义枚举时,成员名称不允许重复
- 如果要限制定义枚举时,不能定义相同值的成员。可以使用装饰器@unique【要导入unique模块】
当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份:
JAN = 1
FEB = 2
MAR = 3
...
NOV = 11
DEC = 12
好处是简单,缺点是类型是int
,并且仍然是变量。
最好的方法是定义一个枚举类:
from enum import Enum, unique
@unique
class Money(Enum):
ONE = 1
# ONE2 = 1 #unique不允许重复值
TWO = 2
FHREE = 3
print(Money.ONE.name)
print(Money.ONE.value)
输出信息:
ONE
1
遍历枚举类
from enum import Enum, unique
@unique
class Money(Enum):
ONE = 1
TWO = 2
FHREE = 3
FOUR = 4
FIVE = 5
SIX = 6
for item in Money:
print(item)
print(item.value)
输出信息:
Money.ONE
1
Money.TWO
2
Money.FHREE
3
Money.FOUR
4
Money.FIVE
5
Money.SIX
6