1. 元類
1.1 理解類、對象
Python中一切皆對象。 --> 所以類也是對象 --> 那麼,誰創建了類? --> 元類
元類:類的類,也就是類的模板。元類的實例爲類,正如類的實例爲對象。
- 分支語句動態創建類:結合分支語句等,使用class關鍵字動態的創建類。當使用class關鍵字時,Python解釋器自動創建這個對象。
- type函數動態創建類:
- type函數功能一:判斷對象的類型。–> eg:
type('abc')
- type函數功能二:動態的創建類。type可以接受一個類的描述作爲參數,然後返回一個類。
- type函數功能一:判斷對象的類型。–> eg:
# type函數創建類:
# 語法:type(函數名, (父類, ), {屬性, 函數, ...})
def hello():
print("--hello--")
myclass = type("MyClass", (object, ), {'my_args': 'haha', 'hello': hello})
print(myclass.my_args)
myclass.hello()
1.2 元類的使用場景
元類的使用:
- 攔截類的創建
- 修改類
- 返回修改之後的類
動態生成類,不能控制類是如何生成的。python3 的metaclass可動態創建類。
4). 很多Web框架都會使用metaclass 來創建類。掌握元類對理解源代碼至關重要。eg: ORM框架類
應用:自定義元類實現單例模式
class Singleton(type):
"""自定義元類實現單例模式"""
cache = {}
def __call__(cls):
if cls not in cls.cache:
cls.cache[cls] = super(Singleton, cls).__call__()
return cls.cache[cls]
class Person(object, metaclass=Singleton): # metaclass一個指定元類的參數
pass
if __name__ == '__main__':
# 注意:Person是Singleton元類實例化出的對象, Person()就是對象(), 執行Singleton.__call__魔術方法.
p1 = Person()
p2 = Person()
print("單例模式是否成功:", p1 is p2)
2. 抽象基類
抽象基類有兩個特點:
- 規定繼承類 (子類) 必須具有抽象基類中指定的方法,即用@abc.abstractmethod裝飾的方法
- 抽象基類無法實例化
基於上述兩個特點,抽象基類主要用於接口設計
實現抽象基類可以使用內置的abc模塊
代碼示例:
import abc
class Human(metaclass=abc.ABCMeta):
# 該裝飾器規定:繼承抽象類的子類在定義時必須要定義的抽象方法
@abc.abstractmethod
def introduce(self):
print("__抽象類__")
# 因爲沒有@abc.abstractmethod裝飾器,在子類中hello方法不是必須的
def hello(self):
print("__hello__")
class Person(Human):
# 1. 規定子類中必須具有抽象基類指定的方法
def introduce(self):
print("__子類__抽象類__")
# 2. 抽象基類無法實例化
# TypeError: Can't instantiate abstract class Human with abstract methods introduce
# h = Human()
p = Person()
p.introduce()
3. 自省機制
自省機制:
- 在日常生活中,自省 (introspection) 是一種自我檢查行爲。
- 在計算機編程中,自省是指這種能力:檢查某些事物以確定它是什麼、它知道什麼以及它能做什麼。自省向程序員提供了極大的靈活性和控制力。
- 例如Python、Ruby、object-C、C++都有自省的能力,這裏面的C++的自省的能力最弱,只能夠知道是什麼類型,而像Python可以知道是什麼類型,還有什麼屬性。
Python中常見的自省機制:
函數用法有: dir()、type()、hasattr(),、setattr()、getattr()、delattr()、isinstance(),通過這些函數,我們能夠在程序運行時得知對象的類型,判斷對象是否存在某個屬性,訪問對象的屬性。
>>> s = "abc"
# 1. 查看 s 的全面屬性和方法
>>> dir(s)
['__add__',
'__class__',
'__contains__',
'__delattr__',
...
'translate',
'upper',
'zfill']
# 2. 返回對象類型
>>> type(s)
str
# 3. 判斷對象類型,返回bool值
>>> isinstance(s, str)
True
>>> def hello():
...: pass
# 4. 設置對象屬性,如 hello.name = 'xiaohong'
>>> setattr(hello, "name", "xiaohong")
# 5. 判斷對象中是否包含某個屬性,返回bool值
>>> hasattr(hello, "name")
True
# 6. 獲取對象中的某個屬性值
>>> getattr(hello, "name")
'xiaohong'
# 7. 刪除對象中的某個屬性
>>> delattr(hello, "name")
>>> hasattr(hello, "name")
False
4. __slots__
Python是動態語言,動態語言與靜態語言的不同之處是:
- 動態語言:可以在運行的過程中,修改代碼
- 靜態語言:編譯時已經確定好代碼,運行過程中不能修改
如何要限制實例中的屬性
- Python允許在定義class的時候,定義一個特殊的 __slots__變量,來限制該class實例能添加的屬性。
- 使用__slots__要注意,__slots__定義的屬性僅對當前類實例起作用,對繼承的子類是不起作用的
代碼示例:
import time
class Date(object):
# 限制示例中能夠添加的屬性有:'__year', '__month', '__day'
__slots__ = '__year', '__month', '__day'
def __init__(self, year, month, day):
self.__year = year
self.__month = month
self.__day = day
@property
def year(self):
return self.__year
@property
def month(self):
return self.__month
@property
def day(self):
return self.__day
@classmethod
def today(cls):
time_tuple = time.localtime()
return cls(time_tuple.tm_year, time_tuple.tm_mon, time_tuple.tm_mday) # 返回實例化對象
def __str__(self):
return "%s-%s-%s" % (self.year, self.month, self.day)
if __name__ == '__main__':
date = Date(2020, 1, 4)
print("date數據類型: ", type(date))
print("date有year屬性嗎?", hasattr(date, "year"))
print("date有time屬性嗎?", hasattr(date, "time"))
# setattr(date, "time", "2020-11-11") # AttributeError: 'Date' object has no attribute 'time'
# print(getattr(date, "time"))
today = date.today()
print("today數據類型: ", type(today))
# print("今天是%s年%s月的第%s天" % (today.year, today.month, today.day))
print(today)
執行結果: