元類、反射及雙線方法
元類
print(type('abc'))
print(type(True))
print(type(100))
print(type([1, 2, 3]))
print(type({'name': '太白金星'}))
print(type((1,2,3)))
print(type(object))class A:passprint(isinstance(object,type))
print(isinstance(A, type))
type元類是獲取該對象從屬于的類,而type類比較特殊,Python原則是:一切皆對象,其實類也可以理解為'對象',而type元類又稱作構建類,python中大多數內置的類(包括object)以及自己定義的類,都是由type元類創造的。
而type類與object類之間的關系比較獨特:object是type類的實例,而type類是object類的子類,這種關系比較神奇無法使用python的代碼表述,因為定義其中一個之前另一個必須存在。所以這個只作為了解。
反射
反射是指程序可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。
python面向對象中的反射:通過字符串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)
反射中常用的四個函數:
class Student:f = '類的靜態變量'def __init__(self,name,age):self.name=nameself.age=agedef say_hi(self):print('hi,%s'%self.name)
obj=Student('alex',16)# hasatter 檢測是否含有某屬性,方法
print(hasattr(obj,'name'))
print(hasattr(obj,'say_hi'))
結果:
True
True# 獲取屬性,方法 # 獲取不到直接報錯
n=getattr(obj,'name')
print(n)
func=getattr(obj,'say_hi')
func()
結果:
'alex'
'hi,alex'# 設置屬性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))
結果:
{'name': 'alex', 'age': 16, 'sb': True, 'show_name': <function <lambda> at 0x00000180E591BB70>}
alexsb# 刪除屬性
delattr(obj,'age')
# delattr(obj,'show_name')
# delattr(obj,'show_name111')#不存在,則報錯print(obj.__dict__)
結果:
{'name': 'alex'}
對類的反射:
class Foo(object):staticField = "old boy"def __init__(self):self.name = 'wupeiqi'def func(self):return 'func'@staticmethoddef bar():return 'bar'print(getattr(Foo, 'staticField'))
print(getattr(Foo, 'func'))
print(getattr(Foo, 'bar'))
# 結果:
old boy
<function Foo.func at 0x00000212D21FBC80> # 獲取func對象方法地址
<function Foo.bar at 0x00000212D21FBD08> # 獲取bar靜態方法地址
對當前模塊的反射:
import sysdef s1():print('s1')def s2():print('s2') this_module = sys.modules[__name__]print(hasattr(this_module, 's1'))
print(getattr(this_module, 's2'))
# 結果:
True
<function s2 at 0x000002000247BBF8>
對其他模塊的反射:
import time
print(hasattr(time,'ctime'))
print(getattr(time,'ctime'))
print(getattr(time,'ctime')())
# 結果:
True
<built-in function ctime>
'Fri Aug 9 08:12:54 2019'
反射的應用
# 沒學反射之前的解決方式:
class User:def login(self):print('歡迎來到登錄頁面')def register(self):print('歡迎來到注冊頁面')def save(self):print('歡迎來到存儲頁面')while 1:choose = input('>>>').strip()if choose == 'login':obj = User()obj.login()elif choose == 'register':obj = User()obj.register()elif choose == 'save':obj = User()obj.save()
# 學了反射之后解決方式
class User:def login(self):print('歡迎來到登錄頁面')def register(self):print('歡迎來到注冊頁面')def save(self):print('歡迎來到存儲頁面')user = User()
while 1:choose = input('>>>').strip()if hasattr(user,choose):func = getattr(user,choose)func()else:print('輸入錯誤。。。。')
函數和方法的區別
通過打印函數(方法)名確定
def func():passprint(func) # <function func at 0x00000260A2E690D0>class A:def func(self):passprint(A.func) # <function A.func at 0x0000026E65AE9C80>
obj = A()
print(obj.func) # <bound method A.func of <__main__.A object at 0x00000230BAD4C9E8>>
通過types模塊驗證
from types import FunctionType
from types import MethodTypedef func():passclass A:def func(self):passobj = A()
print(isinstance(func,FunctionType)) # True
print(isinstance(A.func,FunctionType)) # True
print(isinstance(obj.func,FunctionType)) # False
print(isinstance(obj.func,MethodType)) # True
靜態方法是函數
from types import FunctionType
from types import MethodTypeclass A:def func(self):pass@classmethoddef func1(self):pass@staticmethoddef func2(self):pass
obj = A()# 靜態方法其實是函數
print(isinstance(A.func2,FunctionType)) # True
print(isinstance(obj.func2,FunctionType)) # True
函數和方法的幾點區別:
(1)函數的是顯性傳參的,方法是隱性傳參的。
(2)函數則跟對象無關。
(3)方法可以操作類內部的數據。
(4)方法跟對象是關聯的。
雙線方法
定義:雙下方法是特殊方法,他是解釋器提供的 由雙下劃線加方法名加雙下劃線 __方法名__的具有特殊意義的方法,雙下方法主要是python源碼程序員使用的,在開發中盡量不要使用雙下方法,熟知雙下劃線方法有助于我們研究源碼
**1、__len__**
class B:def __len__(self):print(666)b = B()
len(b) # len 一個對象就會觸發 __len__方法。class A:def __init__(self):self.a = 1self.b = 2def __len__(self):return len(self.__dict__)
a = A()
print(len(a))
**2、__hash__**
class A:def __init__(self):self.a = 1self.b = 2def __hash__(self):return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))
**3、__str__**
如果一個類中定義了__str__方法,那么在打印 對象 時,默認輸出該方法的返回值。
class A:def __init__(self):passdef __str__(self):return '太白'
a = A()
print(a)
print('%s' % a)
**4、__repr__**
如果一個類中定義了__repr__方法,那么在repr(對象) 時,默認輸出該方法的返回值。
class A:def __init__(self):passdef __repr__(self):return '太白'
a = A()
print(repr(a))
print('%r'%a)
**5、__call__**
對象后面加括號,觸發執行。
注:構造方法__new__的執行是由創建對象觸發的,即:對象 = 類名() ;而對于 call 方法的執行是由對象后加括號觸發的,即:對象() 或者 類()()
class Foo:def __init__(self):passdef __call__(self, *args, **kwargs):print('__call__')obj = Foo() # 執行 __init__
obj() # 執行 __call__
**6、__eq__**
class A:def __init__(self):self.a = 1self.b = 2def __eq__(self,obj):if self.a == obj.a and self.b == obj.b:return True
a = A()
b = A()
print(a == b)
**7、__del__**
析構方法,當對象在內存中被釋放時,自動觸發執行。
注:此方法一般無須定義,因為Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
class A:def __init__(self):self.x = 1print('in init function')def __new__(cls, *args, **kwargs):print('in new function')return object.__new__(A, *args, **kwargs)a = A()
print(a.x)
**8、__new__**
class A:def __init__(self):self.x = 1print('in init function')def __new__(cls, *args, **kwargs):print('in new function')return object.__new__(A, *args, **kwargs)a = A()
print(a.x)
單例模式:
class A:__instance = Nonedef __new__(cls, *args, **kwargs):if cls.__instance is None:obj = object.__new__(cls)cls.__instance = objreturn cls.__instance
單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易于外界訪問,從而方便對實例個數的控制并節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
【采用單例模式動機、原因】
對于系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個打印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內容完全一致,則是重復對象,浪費內存資源;如果這些窗口顯示的內容不一致,則意味著在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個才是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
如何保證一個類只有一個實例并且這個實例易于被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,并且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。
【單例模式優缺點】
【優點】
一、實例控制
單例模式會阻止其他對象實例化其自己的單例對象的副本,從而確保所有對象都訪問唯一實例。
二、靈活性
因為類控制了實例化過程,所以類可以靈活更改實例化過程。
【缺點】
一、開銷
雖然數量很少,但如果每次對象請求引用時都要檢查是否存在類的實例,將仍然需要一些開銷。可以通過使用靜態初始化解決此問題。
二、可能的開發混淆
使用單例對象(尤其在類庫中定義的對象)時,開發人員必須記住自己不能使用new關鍵字實例化對象。因為可能無法訪問庫源代碼,因此應用程序開發人員可能會意外發現自己無法直接實例化此類。
三、對象生存期
不能解決刪除單個對象的問題。在提供內存管理的語言中(例如基于.NET Framework的語言),只有單例類能夠導致實例被取消分配,因為它包含對該實例的私有引用。在某些語言中(如 C++),其他類可以刪除對象實例,但這樣會導致單例類中出現懸浮引用
**9、 __item__系列**
class Foo:def __init__(self,name):self.name=namedef __getitem__(self, item):print(self.__dict__[item])def __setitem__(self, key, value):self.__dict__[key]=valuedef __delitem__(self, key):print('del obj[key]時,我執行')self.__dict__.pop(key)def __delattr__(self, item):print('del obj.key時,我執行')self.__dict__.pop(item)f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
10 上下文管理器相關
__enter__ __exit__
# 如果想要對一個類的對象進行with as 的操作 不行。
class A:def __init__(self, text):self.text = textwith A('大爺') as f1:print(f1.text)
class A:def __init__(self, text):self.text = textdef __enter__(self): # 開啟上下文管理器對象時觸發此方法self.text = self.text + '您來啦'return self # 將實例化的對象返回f1def __exit__(self, exc_type, exc_val, exc_tb): # 執行完上下文管理器對象f1時觸發此方法self.text = self.text + '這就走啦'with A('大爺') as f1:print(f1.text)
print(f1.text)
自定義文件管理器
class Diycontextor:def __init__(self,name,mode):self.name = nameself.mode = modedef __enter__(self):print("Hi enter here!!")self.filehander = open(self.name,self.mode)return self.filehanderdef __exit__(self,*para):print("Hi exit here")self.filehander.close()with Diycontextor('py_ana.py','r') as f:for i in f:print(i)
相關面試題:
class StarkConfig:def __init__(self,num):self.num = numdef run(self):self()def __call__(self, *args, **kwargs):print(self.num)class RoleConfig(StarkConfig):def __call__(self, *args, **kwargs):print(345)def __getitem__(self, item):return self.num[item]v1 = RoleConfig('alex')
v2 = StarkConfig('太白金星')
# print(v1[1])
# print(v2[2])
v1.run()-------
class UserInfo:passclass Department:passclass StarkConfig:def __init__(self, num):self.num = numdef changelist(self, request):print(self.num, request)def run(self):self.changelist(999)class RoleConfig(StarkConfig):def changelist(self, request):print(666, self.num)class AdminSite:def __init__(self):self._registry = {}def register(self, k, v):self._registry[k] = vsite = AdminSite()
site.register(UserInfo, StarkConfig)
# 1
# obj = site._registry[UserInfo]()# 2
obj = site._registry[UserInfo](100)
obj.run()-------
class UserInfo:passclass Department:passclass StarkConfig:def __init__(self,num):self.num = numdef changelist(self,request):print(self.num,request)def run(self):self.changelist(999)class RoleConfig(StarkConfig):def changelist(self,request):print(666,self.num)class AdminSite:def __init__(self):self._registry = {}def register(self,k,v):self._registry[k] = v(k)site = AdminSite()
site.register(UserInfo,StarkConfig)
site.register(Department,RoleConfig)for k,row in site._registry.items():row.run()-------
class A:list_display = []def get_list(self):self.list_display.insert(0,33)return self.list_displays1 = A()
print(s1.get_list())-------
class A:list_display = [1, 2, 3]def __init__(self):self.list_display = []def get_list(self):self.list_display.insert(0, 33)return self.list_displays1 = A()
print(s1.get_list())------
class A:list_display = []def get_list(self):self.list_display.insert(0,33)return self.list_displayclass B(A):list_display = [11,22]s1 = A()
s2 = B()
print(s1.get_list())
print(s2.get_list())