Python反射和內置方法(雙下方法) 一、反射 1. 什麼是反射 反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,併在Lisp和麵向對象 ...
Python反射和內置方法(雙下方法)
一、反射
什麼是反射
反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,併在Lisp和麵向對象方面取得了成績。
Python面向對象中的反射
通過字元串的形式操作對象相關的屬性。Python中的一切事物都是對象(都可以使用反射)
四個可以實現自省的函數:
hasattr():檢測是否含有某屬性
class Foo: def __init__(self, name, age): self.name = name self.age = age def func(self): pass # 檢測是否含有某屬性 print(hasattr(Foo, "func")) # True f = Foo("dogfa", 18) print(hasattr(f, "name")) # True print(hasattr(f, "gender")) # False
getattr():獲取屬性
class Foo: def __init__(self, name, age): self.name = name self.age = age def func(self): pass @staticmethod def staticfunc(): print("嘿嘿嘿") # 獲取屬性 f = getattr(Foo, "staticfunc") print(f) # <function Foo.staticfunc at 0x0000028DD8EB9D08> f() # 嘿嘿嘿 # 如果要獲取的屬性不存在那麼就會報錯 f2 = getattr(Foo, "func1") # AttributeError: type object 'Foo' has no attribute 'func1'
setattr():設置屬性
class Foo: def __init__(self, name, age): self.name = name self.age = age def func(self): pass @staticmethod def staticfunc(): print("嘿嘿嘿") f = Foo("dogfa", 18) print(f.__dict__) # {'name': 'dogfa', 'age': 18} setattr(f, "name", f.name + "sb") print(f.__dict__) # {'name': 'dogfa_sb', 'age': 18}
defattr():刪除屬性
class Foo: def __init__(self, name, age): self.name = name self.age = age def func(self): pass @staticmethod def staticfunc(): print("嘿嘿嘿") f = Foo("dogfa", 18) print(f.__dict__) # {'name': 'dogfa', 'age': 18} delattr(f, "name") print(f.__dict__) # {'age': 18} # delattr(f, "gender") # 刪除不存在的屬性時會報錯
使用反射的場景
- 對對象的反射
- 對類的反射
- 其它模塊的反射
- 當前模塊的反射
二、內置方法
函數與方法的區別
在說方法之前我們先來說一下在Python中函數和方法的區別。
# 通過導入types模塊來驗證 from types import FunctionType, MethodType def func(): pass class A: def func(self): pass @staticmethod def func2(): pass obj = A() # FunctionType:函數 MethodType:方法 print(isinstance(func, FunctionType)) # True print(isinstance(A.func, FunctionType)) # True print(isinstance(obj.func, FunctionType)) # False print(isinstance(obj.func, MethodType)) # True print(isinstance(A.func2, FunctionType)) # True print(isinstance(obj.func2, FunctionType)) # True # 通過列印函數(方法)名驗證 from types import FunctionType, MethodType def func(): pass class A: def func(self): pass @staticmethod def func2(): pass obj = A() print(func) # <function func at 0x000002013BC32E18> print(A.func) # <function A.func at 0x000002013BF6A598> print(obj.func) # <bound method A.func of <__main__.A object at 0x000002013BDF7DD8>>
總結:
(1)函數的是顯式傳遞數據的。如我們要指明為len()函數傳遞一些要處理數據。
(2)函數則跟對象無關。
(3)方法中的數據則是隱式傳遞的。
(4)方法可以操作類內部的數據。
(5)方法跟對象是關聯的。如我們在用strip()方法時,都是要通過str對象調用,比如我們有字元串s,然後s.strip()這樣調用。是的,strip()方法屬於str對象。
(6)靜態方法就是函數
內置方法(雙下方法)
定義:雙下方法是特殊方法,他是解釋器提供的 由爽下劃線加方法名加雙下劃線 __方法名__的具有特殊意義的方法,雙下方法主要是Python源碼程式員使用的,我們在開發中儘量不要使用雙下方法,但是深入研究雙下方法,更有益於我們閱讀源碼。
調用:不同的雙下方法有不同的觸發方式,就好比盜墓時觸發的機關一樣,不知不覺就觸發了雙下方法,例如:__init__
item系列
class Foo: def __init__(self, name, age): self.name = name self.age = age def __getitem__(self, item): print("執行obj['key']時會執行我") return getattr(self, item) def __setitem__(self, key, value): print("執行obj['key'] = value 時會執行我") setattr(self, key, value) def __delitem__(self, key): print("執行del['key']時會執行我") delattr(self, key) obj = Foo("oldwang", 20) print(obj["name"]) # 執行obj['key']時會執行我 # oldwang obj["name"] = "oldniu" print(obj["name"]) # 執行obj['key'] = value 時會執行我 # 執行obj['key']時會執行我 # oldniu print(obj.__dict__) del obj["name"] print(obj.__dict__) # {'name': 'oldniu', 'age': 20} # 執行del['key']時會執行我 # {'age': 20}
__del__
析構方法,當對象在記憶體中被釋放時,自動觸發執行。
註:此方法一般無須定義,因為Python是一門高級語言,程式員在使用時無需關心記憶體的分配和釋放,因為此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
class FileHandle: def __init__(self, file_path): self.f = open(file_path, mode="r", encoding="utf-8") def read(self): return self.f.read(1024) def __del__(self): # 在程式執行完時釋放文件句柄 self.f.close() f = FileHandle("file/userinfo") print(f.read())
__new__
# 單例類 class Single: __isinstance = None def __new__(cls, *args, **kwargs): if not cls.__isinstance: cls.__isinstance = object.__new__(cls) return cls.__isinstance return cls.__isinstance one = Single() two = Single() print(one is two) # True
__call__
對象後面加括弧,觸發執行。
註:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括弧觸發的,即:對象() 或者 類()()
class Foo: def __call__(self, *args, **kwargs): print("執行__call__") f = Foo() f() # 執行__call__ # 還可以這樣寫 Foo()()
__len__
class Foo: def __init__(self, name, age): self.name = name self.age =age def __len__(self): return len(self.__dict__) f = Foo("dogfa", 18) print(len(f)) # 2
__hash__
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(str(self.a)+str(self.b)) a = A() print(hash(a))
__eq__
class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return True a = A() b = A() print(a == b) # True
三、其它
# 有一個員工類,1000個員工對象,對象屬性有姓名,性別,年齡,部門,
# 按姓名,性別對1000個對象去重
class Employee:
def __init__(self, name, age, gender, department):
self.name = name
self.age = age
self.gender = gender
self.department = department
def __hash__(self):
return hash("{0}{1}".format(self.name, self.gender))
def __eq__(self, other):
if self.name == other.name and self.gender == other.gender:
return True
employee_list = []
for i in range(250):
employee_list.append(Employee("dogfa", i, "male", "python" + str(i)))
for i in range(250):
employee_list.append(Employee("djb", i, "female", "php" + str(i)))
for i in range(250):
employee_list.append(Employee("oldniu", i, "male", "java" + str(i)))
for i in range(250):
employee_list.append(Employee("cdj", i, "female", "go" + str(i)))
for employee_obj in set(employee_list):
print(employee_obj.__dict__)