Python 元類,MethodType,列舉類
阿新 • • 發佈:2019-02-13
元類
簡介
- 類也是物件,你可以在執行時動態的建立它們,就像其他任何物件一樣;當你使用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