isinstance和issubclass isinstance isinstance(obj1,obj2):判斷對象與類之間的關係,判斷第一個參數是否是第二個參數的實例。 type()函數和isinstance()函數兩者有什麼區別呢? issubclass issubclass(obj1,obj ...
isinstance和issubclass
isinstance
isinstance(obj1,obj2):判斷對象與類之間的關係,判斷第一個參數是否是第二個參數的實例。
>>> n1 = 10 >>> isinstance(n1, int) #判斷n1是否是數字類型,如果是返回True如果不是防護False True >>> class A(object): ... pass ... >>> a1 = A() >>> isinstance(a1, A) # 判斷a1是否是類A的對象,如果是返回True,如果不是返回False True
type()函數和isinstance()函數兩者有什麼區別呢?
>>> print(type(1) is int) True >>> print(isinstance(1, int)) True #從上面的結果看,兩者的結果都是True,那麼type()與isinstance()的區別在哪呢? #從接下來的例子,就能夠清晰看出來。 class A:pass class B(A):pass b = B() print(isinstance(b, A)) # True print(isinstance(b, B)) # True print(type(b)) # <class '__main__.B'> print(type(b) is B) # True print(type(b) is A) # False #總結: isinstance()是可以用在繼承的關係上;而type()不能用來檢測繼承關係。
issubclass
issubclass(obj1,obj2):用來描述一個類與另一個類之間的關係,判斷一個類是否是另一個類的子類
class A:pass class B(A):pass print(issubclass(B, A)) # True print(issubclass(A, B)) # False # 總結: 類B是類A的子類,類A不是類B的子類
反射
什麼是反射?
反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,併在Lisp和麵向對象方面取得了成績。
Python中的反射:通過字元串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)
四個可以實現自省的函數:hasattr getattr setattr delattr
下列方法適用於類和對象(一切皆對象,類本身也是一個對象)
getattr()
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ passgetattr
具體使用:
(1)getattr(obj,屬性名)----> 傳入字元串類型的屬性名,返回屬性的值,相當於執行了“obj.屬性名”
(2)getattr(obj,方法名)----> 傳入字元串類型的方法名,返回對應方法的記憶體地址。
謹記:如果第二個參數不存在的話,就會報錯。
class A: name = "小白" age = 18 def func(self): print("in the func") print(A.name) print(A.age) # 使用反射的形式,操作類的屬性 print(getattr(A, 'name')) # 使用反射操作類中的方法 a = A() ret = getattr(a, 'func') # 得到了func方法的記憶體地址 ret() # 調用func()方法
hasattr()
def hasattr(*args, **kwargs): # real signature unknown """ Return whether the object has an attribute with the given name. This is done by calling getattr(obj, name) and catching AttributeError. """ passhasattr
具體使用:
hasattr(obj,字元串類型的屬性名/方法名)--->判斷對象中是否擁有指定屬性/方法,返回True/False。
謹記:所以hasattr經常和getattr一起使用,先用hasattr判斷,再使用getattr取值。
# 例子1:通過hasattr判斷,再用getattr來取值。 class A: name = "xiao" age = 18 inpu = input('>>>: ') if hasattr(A, inpu): print(getattr(A, inpu)) else: print("A類中沒有對應的屬性") >>>: name xiao >>>: sex A類中沒有對應的屬性 # 例子2: class Student: def __init__(self, name, age): self.name = name self.age = age def show(self): for key in self.__dict__: #__dict__查看對象的所有屬性 print(key, self.__dict__[key]) # key self.__dict__[key] stu1 = Student("小白", 18) if hasattr(stu1, 'show'): func = getattr(stu1, 'show') # 返回方法的記憶體地址,賦值給func() func() name 小白 age 18
setattr()
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ passsetattr
具體使用:
setattr(obj,'name',value) ---->通過反射的方式為一個對象設置屬性(增改操作),相當於obj.name=value
# 正常情況下,想給對象設置一個值。 class Student: def __init__(self, name, age): self.name = name self.age = age def show(self): for key in self.__dict__: print(key, self.__dict__[key]) stu1 = Student("小白", 18) stu1.name = "小黃" print(stu1.name) # 小黃 # 除了上面的方式之外,還有一種方式為對象設置屬性,通過反射的形式. setattr(stu1, 'name', '菜農') print(stu1.__dict__) # {'name': '菜農', 'age': 18} print(stu1.name) # 菜農
除了給對象綁定屬性,還可以對象綁定一個函數。
# 創建一個函數,通過setattr給對象綁上這個函數 class Student: def __init__(self, name, age): self.name = name self.age = age def show(self): for key in self.__dict__: print(key, self.__dict__[key]) stu1 = Student("小白", 18) def func(): print('In func') setattr(stu1, 'func1', func) stu1.func1() # In func print(stu1.__dict__) # {'name': '小白', 'age': 18, 'func1': <function func at 0x00000226123740D0>}
delattr()
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ passdelattr
具體使用:
delattr(obj,'name'):通過反射的方式,刪除對象中的屬性(刪除操作)
class Student: def __init__(self, name, age): self.name = name self.age = age def show(self): for key in self.__dict__: print(key, self.__dict__[key]) stu1 = Student("小白", 18) # 正常方法 del stu1.name print(stu1.__dict__) # {'age': 18} # 通過delattr反射的方式 delattr(stu1, 'name') print(stu1.__dict__) # {'age': 18}
類使用反射
類:靜態屬性 類方法 靜態方法
class Student(object): name = "小白" @classmethod def check_course(cls): print("查看課程") @staticmethod def login(): print("登錄") # 檢查是否含有某屬性 print(hasattr(Student, "login")) # 檢測Student類中是否有login方法,如果有返回True,沒有返回False # 通過反射查看靜態屬性 print(getattr(Student, 'name')) # 通過反射調用方法 print(getattr(Student, "check_course")) # 得到的是類方法的記憶體地址 getattr(Student, "check_course")() # 調用類方法 print(getattr(Student, "login")) # 得到的是靜態方法的記憶體地址 getattr(Student, "login")() # 調用靜態方法 # 一般情況hasattr和getattr聯用, 示例: num = input('>>>') # 等待用戶輸入 if hasattr(Student, num): # 判斷用戶輸入的方法是否有,如果有才執行。 getattr(Student, num)()
對象使用反射
class A(object): def __init__(self, name): self.name = name def func(self): print(" In func ") a = A("小白") print(a.name) print(getattr(a, "name")) getattr(a, "func")()
模塊使用反射
import os os.rename('__init__.py', 'init') getattr(os, 'rename')('init', '__init__.py') # os.rename('init', '__init__.py')
自定義模塊使用反射
既然反射能夠操作模塊,那麼當前文件其實也是一個模塊,通過sys.modules可以看出,當前文件就是sys.modules['__main__']。
import sys def wahaha(): print("wahaha") def qqxing(): print("qqxing") wahaha() qqxing() my_file = sys.modules['__main__']
getattr(my_file, 'wahaha')() getattr(my_file, 'qqxing')()
通過sys.modules['__main__']好像完成了要求,但是,仔細想一下,使用__main__表示當前文件,假如在另外一個py文件中把當前文件導入,此時__main__便指向的是另外一個py文件,使用sys.modules['__main__']這種方式顯然是不妥的,那麼要怎麼解決?
使用sys.modules[__name__]就能完美解決,因為__name__就是'__main__'。
#通過sys.modules[__name__]反射當前模塊<br>import sys import sys def func(): print("本模塊函數func") getattr(sys.modules[__name__], 'func')() # 本模塊函數func
總結:
sys.modules['__main__'] : __main__會隨著文件的不同而不同,存在安全隱患。
sys.modules[__name__]:__name__不管在哪裡導入這個模塊,都代表這個文件。
反射總結
(1)hasattr():判斷一個對象中是否能夠調用一個名字,返回True/False
(2)getattr():返回一個對象中的名字的值
(3)setattr():為一個對象設置屬性(增加/修改操作)
(4)delattr():刪除一個對象的某個屬性(刪除操作)
# 反射 # hasattr, getattr # 類名.名字 # getattr(類名, '名字') # 對象名.名字 # getattr(對象, '名字') # 模塊名.名字 # import 模塊 # getattr(模塊, '名字') # 自己文件.名字 # import sys # getattr(sys.modules[__main__], '名字')
反射的應用
#! /usr/bin/env python # -*- coding: utf-8 -*- # __author__ = "yanjieli" # Date: 2018/11/25/025 import sys class Int(object): def __init__(self, name): self.name = name class Manager(Int): OPERATE_LIST = [ ('創建課程', 'create_course'), ('招聘老師', 'create_Teacher'), ('查看課程', 'check_course'), ('查看學生信息', 'check_userinfo'), ] def create_course(self): print("創建課程") def create_Teacher(self): print("招聘老師") def check_course(self): print("查看課程") def check_userinfo(self): print("查看學生信息") class Teacher(Int): OPERATE_LIST = [ ('查看課程', 'check_course'), ('查看學生成績', 'check_achievement'), ] def check_course(self): print("查看課程") def check_achievement(self): print("查看學生成績") class Student(Int): OPERATE_LIST = [ ('查看課程', 'check_course'), ('選擇課程', 'choose_course'), ('查看已選課程', 'choosed_course'), ('jsdklfjskld', 'aa'), ] def check_course(self): print("查看課程") def choose_course(self): print("選擇課程") def choosed_course(self): print("查看已選課程") def login(): username = input('user:>>>') password = input('password:>>>') with open("user_info", encoding="utf-8") as f: for line in f: user, pwd, ident = line.strip().split("|") if username == user and password == pwd: print("歡迎您!%s" % user) return user, ident else: print("登錄失敗") exit() def main(): user, id = login() cls = getattr(sys.modules['__main__'], id) # 通過反射拿到當前登錄用戶所屬的類,如果是學生,則拿到學生類; operate_list = cls.OPERATE_LIST obj = cls(user) for index, i in enumerate(operate_list, 1): print(index, i[0]) # 列印出所有的功能 option = int(input("option: >>>")) option_itme = operate_list[option -1] # 拿到的是一個元組,比如:('創建課程', 'create_course'), getattr(obj, option_itme[1])() # 通過反射拿到所輸入的選項對應的方法並執行 main()反射應用示例
內置方法
內置方法:內置方法就是不需要程式員定義,本身就存在類中的方法。內置方法又稱為雙下方法,魔術方法。
內置方法通常長成這樣:__名字__。 每一個雙下方法都有它自己的特殊意義
所有的雙下方法,都不需要我們直接調用,都有另外一種自動觸發它的方法。而是總有一些其他的 內置方法 特殊的語法 來自動觸發這些 雙下方法
__call__
__call__ 方法的執行是由對象後加括弧觸發的,即:對象()。擁有此方法的對象可以像函數一樣被調用。
class Person: def __init__(self, name, age): self.name = name self.age = age def __call__(self, *args, **kwargs): print('調用對象的__call__方法') a = Person('張三', 24) # 類Person可調用 a() # 對象a可以調用
class A(object): def __call__(self, *args, **kwargs): print("執行了call方法") class B(object): def __init__(self, cls): print("在實例化A之前做一些事情") self.obj = cls() self.obj() print("在實例化A之後做一些事情") a = A() a() # 對象() == 相當於調用__call__方法 A()() # 類名()() 相當於先實例化得到了一個對象,再對對象(), ==> 和上面的結果一樣,相當於調用__call__方法 B(A)
__len__
計算對象的長度,如果類中定義了__len__方法,那麼在對象中就能用內置函數len(obj),就會自動調用__len__方法。
class Foo(object): def __init__(self, s): self.s = s def __len__(self): print('調用了__len__方法, 計算長度') return len(self.s) a = Foo("aaaa") print(len(a)) # 自動調用了__len__方法 ''' 列印結果: 調用了__len__方法, 計算長度 4 '''
__new__
網上經常有一個笑話“程式員可以自己new一個對象”, 到底new有什麼作用呢?
__new__又稱為構造方法,通過__init__()方法,我們知道初始化一個實例需要經歷的幾個步驟中的第一步就是在記憶體空間中開闢一個對象空間,這個對象空間是__init__方法開闢的麽?其實不是,在init之前,實例化對象的第一步是通過__new__方法創建一個對象空間。
class Fuu(): def __new__(cls, *args, **kwargs): # 構造方法,構造了對象的空間 print("執行了__new__方法") return object.__new__(cls) # 或者super().__new(cls) # 調用了object類中的__new__方法 def __init__(self, name, age): # 初始化方法 print("執行了init方法") self.name = name self.age = age c1 = Fuu("小白", 18) """ 就這樣執行可以列印出: 執行了__new__方法 執行了init方法 """
從上面的例子,我們可以更加清晰的得到初始化一個對象所經歷的過程:
1、實例化Fuu類,應該先執行其__new__方法。
2、但是Fuu類中沒有定義__new__方法,所以到object父類中執行object的__new__方法。
回顧調用父類的兩種方法:
object.__new__(cls)
super().__new__(cls)
3、此時在記憶體中開闢一塊對象空間。
4、才執行init初始化方法,把地址傳給self,給對象封裝屬性。
總結:構造方法__new__和初始化方法__init__之間的區別?
形象的說,類好比一個人類型的模板,__new__構造方法就是捏小人的過程(捏出來的每一個小人都是一樣的),__init__初始化方法好比給小人穿衣服的過程(為對象封裝屬性),一定是先有__new__方法,後有__init__。
單例模式:一個類只有一個實例的時候,這種就叫做單例模式。
# 單例模式:一個類只有一個實例。 # 思考:如果使得一個類只能實例化一次,也就是這要控制只能開闢一次空間,就能實現單例模式。 class Singleinstance(): __INSTANCE = None # 通過設置一個標誌位,用來使得只能運行一次new方法 def __new__(cls, *args, **kwargs): if not cls.__INSTANCE: cls.__INSTANCE = object.__new__(cls) return cls.__INSTANCE def __init__(self, name, age): self.name = name self.age = age obj1 = Singleinstance('小白', 18) obj2 = Singleinstance('小黑', 20) print(obj1.name, obj1.age) print(obj2.name, obj2.age) print(obj1) print(obj2) >>> 小黑 20 小黑 20 <__main__.Singleinstance object at 0x000001C1657F6048> <__main__.Singleinstance object at 0x000001C1657F6048> ''' 總結:由上列印可以看到不論實例化多少對象,都是一個對象,都會已最後一個為準。 '''
__str__ 與 __repr__
__str__
如果想把一個類的實例變成str類型,列印對象名的時候就執行__str__方法。
__str__ : str(obj),要求必須實現了__str__,要求這個方法的返回值必須是字元串str類型
三種場景會觸發__str__方法:
- (1)當你列印一個對象名的時候,就會就會觸發__str__。
- (2)當你使用%s格式化輸出對象的時候,也會觸發__str__。
- (3)強制轉換數據類型的時候,也會觸發__str__。
class Student: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __str__(self): return "%s %s %s" % (self.name, self.age, self.sex) st1 = Student("小白", 18, "男") print(st1) #1.列印對象名,自動觸發__str__方法 >>> 小白 18 男 student_list = [] student_list.append(st1) for index, i in enumerate(student_list): print('%s %s'% (index, i)) #2.當使用%s格式化的時候,自動觸發__str__ >>> 0 小白 18 男
__repr__
1、__repr__是__str__方法的備胎,如果有__str__就使用__str__,否則執行__repr__。
# (1)同時存在__str__和__repr__兩個方法: class Student: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def __str__(self): return "str: %s %s %s" % (self.name, self.age, self.sex) def __repr__(self): return "repr: %s %s %s" % (self.name, self.age, self.sex) st1 = Student("小白", 18, "男") print(st1) >>> str: 小白 18 男 # (2)只存在__repr__方法時,再次列印對象名: class Student: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex # 註釋掉__str__雙下方法 # def __str__(self): # return "str: %s %s %s" % (self.name, self.age, self.sex) def __repr__(self): return "repr: %s %s %s" % (self.name, self.age, self.sex) st1 = Student("小白", 18, "男") print(st1) # 當__str__沒有時,執行__repr__方法 >>> repr: 小白 18 男
如果__repr__僅僅只是__str__的備胎,那麼它就沒有存在的意義了。所有__repr__還是有它自己的用武之地的時候:
2、如果使用內置函數repr(obj),或者通過%r格式化的時候,就會自動觸發__repr__方法,不管有沒有__str__,都調用__repr__。
st1 = Student("小白", 18, "男") print(repr(st1)) >>> repr: 小白 18 男
小技巧:
python中之所有既有__str__,又有__repr__,__str__用於顯示給用戶看,__repr__用於顯示給開發人員看。 下麵就有一個偷懶的小辦法:
__str__=__repr__
擴展知識:
class Fcc: def __str__(self): return "Fcc.str" def __repr__(self): return "Fcc.repr" class Ecc(Fcc): def __str__(self): return "Ecc.str" def __repr__(self): return "Ecc.repr" E1 = Ecc() print(E1) >>> Ecc.str ''' (1) 先去子類中查找,先調用__str__方法。 (2) 如果把子類的__str__方法註釋掉,會去父類中查找父類的__str__方法 (3) 如果把父類的__Str__的方法註釋掉,會再回到子類中執行備胎__repr__方法。 '''
__del__
__del__ 析構方法:在刪除某個類的對象的時候,就會觸發這個雙下方法,再刪除這個對象。即“del obj”執行這個刪除對象的代碼的時候,觸發__del__析構方法。
運用的場景:文件資源,網路資源等。
class