1. 程式人生 > >Python 元類,MethodType,列舉類

Python 元類,MethodType,列舉類

元類

簡介

  1. 類也是物件,你可以在執行時動態的建立它們,就像其他任何物件一樣;當你使用class關鍵字時,Python直譯器自動建立這個物件
  2. 元類就是用來建立類的“東西”;元類就是類的類
  3. 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建立類

  1. type() 函式可以檢視物件型別
  2. 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__
  1. Python允許在定義class的時候,定義一個特殊的__slots__變數,來限制該class例項能新增的屬性
  2. 使用__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

列舉類

  1. 定義列舉時,成員名稱不允許重複
  2. 如果要限制定義列舉時,不能定義相同值的成員。可以使用裝飾器@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