您好,登錄后才能下訂單哦!
?
目錄
面向對象三要素:... 3
類對象及類屬性:... 4
實例化:... 6
instance實例對象:... 10
裝飾一個類:... 13
類方法、靜態方法:... 14
訪問控制:... 16
猴子補丁:... 19
?
?
?
面向對象
?
語言的分類:
面向機器:
抽象成機器的指令,機器容易理解;
代表:匯編語言;
?
面向過程:
流程式,第1步,第2步...;
問題規模小,可以步驟化,按部就班處理;
代表:C語言;
?
面向對象:
隨著計算機需要解決的問題的規模擴大,情況越來越復雜,需要很多人、很多部門協作,面向過程編程不太適合了;
代表:C++、java、python;
?
什么是面向對象?
一種認識世界,分析世界的方法論,將萬事萬物抽象為類;
?
class類,是抽象的概念,是萬事萬物的抽象,是一類事物的共同特征的集合;
用計算機語言來描述類,就是屬性和方法的集合;
?
instance實例,或,object對象,是類的具象,是一個實體;
對于我們每個人這個個體,都是抽象概念人類的不同的實體;
例:
你吃魚:
你,就是對象;
魚,也是對象;
吃,就是動作;
你是具體的人,是具體的對象,你屬于人類,人類是個抽象的概念,是無數具體的個體的抽象;
魚也是具體的對象,就是你吃的這一條具體的魚,這條魚屬于魚類,是無數的魚抽象出來的概念;
?
注:
數據<-->屬性;
動作<-->方法;
屬性和方法的集合體;
python中函數和方法要加以區分;
屬性也決定著方法的多少,有一些方法是為內部屬性作操作的,有一些方法是對外的;
項目有多復雜,數據結構就有多復雜;
類是數據和動作,即屬性和方法的集合;
抽象?(數據和動作的集合);
具象?
吃,是動作,是操作,也是方法,這個吃是你的動作,也就是人類具有的方法;如果反過來,魚吃人,吃就是魚類的動作了;
吃,這個動作,很多動物都具有,人類和魚類都屬于動物類,而動物類是抽象的概念,是動物都有吃的動作,但吃法不同而已;
你駕駛車,這個車是車類的具體的對象(實例),駕駛這個動作是魚類不具有的,是人類具有的方法;
?
屬性,是對象狀態的抽象,用數據結構來描述;
操作(方法),是對象行為的抽象,用操作名和實現該操作的方法來描述;
?
每個人都有名字、身高、體重等信息,這些信息是個人的屬性,但這些信息不能保存在人類中,因為人類是抽象的概念,不能保留具體的值;
而人類的實例,是具體的人,他可以存儲這些具體的屬性,而且不同人有不同的屬性;
?
哲學:
一切皆對象;
對象是數據(屬性)和操作(方法)的封裝;
對象是獨立的,但對象之間可相互作用;
目前,面向對象是最接近人類認知的編程范式;
?
UML,unified modeling language,統一建模語言;
?
?
?
1、封裝:
組裝:將數據和操作組裝到一起;
隱藏數據:對外只暴露一些接口,通過接口訪問對象;(如駕駛員使用汽車,不需要了解汽車的構造細節,只需要知道使用什么部件怎么駕駛就行,踩了油門就能跑,可以不了解背后的機動原理);
?
encapsulation封裝:
面向對象的三要素之一;
將數據和操作組織到類中,即屬性和方法;
將數據隱藏起來,給使用者提供操作,使用者通過操作就可獲取或修改數據,getter,setter;
通過訪問控制,暴露適當的數據和操作給用戶,該隱藏的隱藏起來,保護成員或私有成員;
?
?
2、繼承:
多復用,繼承是為了復用,繼承來的就不需要自己寫了;
多繼承少修改,ocp,open-closed-principle開閉原則,使用繼承來改變,來體現個性;
多繼承慎用,問題失控,計算機所用技術,簡單就是美的;
?
注:
函數復用,如yield from;
?
3、多態:
面向對象編程最靈活的地方,動態綁定;
python運行時才綁定(知道)類型;
?
人類就是封裝;
人類繼承自動物類,孩子繼承父母的特征,分為單一繼承、多繼承;
多態,繼承自動物類的人類,貓類的操作“吃”不同;
其它語言的繼承、多態與python不一樣,僅封裝一樣;
?
在面向對象中,父類、子類通過繼承聯系在一起,如果可通過一套方法,就可實現不同表現,就是多態;
一個類繼承自多個類,就是多繼承,它將具有多個類的特征;
?
?
?
python的類:
定義:
class ClassName:
???????? 語句塊
必須使用class關鍵字;
類名必須用大駝峰(首字母大寫)命名,習慣,不是語法強制;
類定義完成后,就產生了一個類對象,綁定到了ClassName上;
?
注:
區分:類對象和類的對象;
?
例:
class MyClass:
??? '''a example class'''
??? x = 'abc'?? #類屬性,另有對象屬性(類的實例的屬性)
??? def foo(self):?? #類屬性foo(單從標識符講,foo是類屬性),同時也是方法,self必須作為第1個參數
??????? print(self.x)
??????? return 'My class'
?
print(MyClass)
print(type(MyClass))
print(MyClass.__name__)
print(MyClass.x)?? #不是內存地址,高級語言對能簡化的就簡化了
print(MyClass.foo)?? #內存地址
print(MyClass().foo())
print(MyClass.__doc__)
輸出:
<class '__main__.MyClass'>
<class 'type'>
MyClass
abc
<function MyClass.foo at 0x7f05be5370d0>
abc
My class
a example class
?
?
?
類對象,類的定義就會生成一個類對象;
?
類的標識符:
類的屬性,類中定義的變量和類中定義的方法都是類的屬性;
類變量,上例中x是MyClass的變量;
?
MyClass中,x、foo都是類的屬性,__doc__也是類的屬性;
foo方法是類的屬性,如同吃是人類的方法,但是每一個具體的人才能吃東西,也就是說吃是人類的實例才能調用的方法;
foo是method方法對象,不是普通的function函數對象,它必須至少有一個參數,且第一個參數必須是self(self可換名字),這個參數位置就留給了self;
?
self指代當前實例本身,self這個名字只是一個慣例,它可以修改,但請不要修改,否則影響代碼的可讀性;
?
注:
inspect中,isfunction()、ismethod();
?
def bar():
class MyClass:
???????? example = bar?? #語法雖允許,但不要這樣寫,破壞了封裝,不符合編程規范,方法就寫在類內部
?
例:
class MyClass:
??? '''a example class'''
??? x = 'abc'
??? def foo(self):
??????? # print(self.x)
??????? print(self)
??????? # return 'My class'
?
# print(MyClass)
# print(type(MyClass))
# print(MyClass.__name__)
# print(MyClass.x)
# print(MyClass.foo)
print(MyClass().foo())
# print(MyClass.__doc__)
print(MyClass.foo(1))?? #不判斷self的類型,1、None
?
mycls = MyClass()?? #實例化、初始化
print(mycls.foo())?? #對象調用方法,相當于悄悄的把mycls放入foo(mycls)中
print(mycls.x)?? #實例可拿走類屬性
print(mycls.foo)?? #對象綁定方法
輸出:
<__main__.MyClass object at 0x7f338d4ff438>
None
1
None
<__main__.MyClass object at 0x7f338d4ff438>
None
abc
<bound method MyClass.foo of <__main__.MyClass object at 0x7f338d4ff438>>
?
例:
class MyClass:
??? '''a example class'''
??? x = 'abc'
??? def foo(self):?? #self可改名
??????? # print(self.x)
??????? # print(self)
??????? print(id(self))
??????? # return 'My class'
??????? # return self
?
mycls = MyClass()
print(mycls.foo())
# print(mycls.x)
# print(mycls.foo)
print(id(mycls))
輸出:
140426199077888
None
140426199077888
?
常用:
print(MyClass.x)
mycls = MyClass()
print(mycls.x)
print(mycls.foo())
?
?
?
mycls = MyClass(),
在類對象名稱后面加(),就調用類的實例化方法,完成實例化;
實例化,就真正創建一個該類的對象(實例),如人類的實例tom,jerry;
實例化后,獲得的實例,是不同的實例,即使是使用同樣的參數實例化,也得到不一樣的對象;
python類實例化后,會自動調用__init__(self)方法,這個方法第一個參數必須留給self,其它參數隨意;
?
__init__(self)方法:
MyClass實際上調用的是__init__(self)方法,可以不定義,如果沒有定義會在實例化后隱式調用;
作用:對實例進行初始化;
__init__(self)方法與其它方法不一樣,返回值只能是None,一般不寫返回值,如果寫return只能兩種:return和return None,其它形式一律報錯;
初始化函數可以多個參數,第一個參數必須self;
初始化函數也稱構造器,構造方法,僅初始化,構造實例是__new__方法;
?
另,__new__(cls,*args,**kwargs),用于構建實例,極少用,類方法;
?
__init__(self)方法,默認的語句是:
def __init__(self):
???????? pass
?
例:
class MyClass:
??? '''this is a example class'''
??? x = 123
??? def __init__(self):
??????? print('init')?? #自定義初始化
?
??? def foo(self):
??????? return 'foo = {}'.format(self.x)
?
a = MyClass()
print(a.foo())
輸出:
init
foo = 123
?
例:
class MyClass:
??? '''this is a example class'''
??? def __init__(self):
??????? print('self in init = {}'.format(id(self)))?? #self就是調用者,即實例對象c
c = MyClass()
print('c = {}'.format(id(c)))
輸出:
self in init = 140190037407448
c = 140190037407448
?
例:
class Person:
??? x = 'abc'
??? def __init__(self,name,age=18):
??????? self.name = name?? #實例屬性,對象的屬性
??????? self.age = age
?
??? def showage(self):
??????? print('{} is {}'.format(self.name,self.age))
?
tom = Person('tom')
jerry = Person('jerry',20)
print(tom.name,tom.age)
print(jerry.name,jerry.age)
print(tom.x,jerry.x)
輸出:
tom 18
jerry 20
abc abc
?
例:
class Person:
??? x = 'abc'
??? def __init__(self,name,age=18):
??????? self.name = name
??????? self.age = age
?
??? def showage(self):
??????? print('{} is {}'.format(self.name,self.age))
?
tom = Person('tom')
# jerry = Person('jerry',20)
# print(tom.name,tom.age)
# print(jerry.name,jerry.age)
# print(tom.x,jerry.x)
# print(tom == jerry)
jerry = Person('tom')
print(tom.name,tom.age)
print(jerry.name,jerry.age)
print(tom == jerry)?? #tom和jerry是不同的個體,盡管初始化時參數一樣
print(tom is jerry)
輸出:
tom 18
tom 18
False
False
?
例:
class Person:
??? x = 'abc'
??? def __init__(self,name,age=18):
??????? self.name = name
??????? # self.age = age
??????? self.y = age
?
??? def showage(self,x,y):?? #showage中的形參y和self.y不一樣,self.y是實例在外部使用時用的,y是形參
??????? print('{} is {}. {} {}'.format(self.name,self.y,x,y))
??????? self.y = x
??????? Person.x = x?? #類中的所有方法,包括特殊__init__(self)方法,都可對類屬性或實例屬性進行修改
?
tom = Person('tom')
jerry = Person('jerry',20)
# print(tom.name,tom.age)
# print(jerry.name,jerry.age)
# print(tom.x,jerry.x)
# print(tom == jerry)
# jerry = Person('tom')
# print(tom.name,tom.age)
# print(jerry.name,jerry.age)
# print(tom == jerry)
# print(tom is jerry)
print(tom.y,jerry.y)
tom.showage(100,'a')
jerry.showage(200,'b')
print(tom.y,jerry.y)
print(Person.x)
print(x)?? #當前作用域中沒有x,也說明x在Person類中封裝著,要訪問x前面要加限定,如Person.x
輸出:
18 20
Traceback (most recent call last):
tom is 18. 100 a
jerry is 20. 200 b
100 200
200
? File "/home/python/magedu/projects/cmdb/example_class_Person.py", line 29, in <module>
??? print(x)
NameError: name 'x' is not defined
?
?
?
類初始化后一定會獲得一個對象,就是實例對象;
tom、jerry就是Person類的實例;
__init__方法的第一個參數self,就是指代某一個實例;
類實例化出一個實例對象,實例對象會綁定方法,調用方法時采用jerry.showage()的方式;
在定義showage(self)時,不能少了self,這個self就是jerry,python會把方法的調用者作為第一個參數self的實參傳入;
self.name就是jerry對象的name,name是保存在了jerry對象上,而不是Person類上,所以稱為實例變量;
?
實例變量是每一個實例自己的變量,是自己獨有的;
類變量是類的變量,是類的所有實例共享的屬性和方法;
?
類屬性保存在類的__dict__中;
實例屬性保存在實例的__dict__中;
如果從實例訪問類的屬性,需要借助__class__找到所屬的類;
?
特殊屬性:
__name__,對象名;
__class__,對象的類型,python3中__class__和type()結果一樣;
__dict__,對象的屬性的字典;
__qualname__,類的限定名;
?
總結:
是類的,也是這個類所有實例的,其實例都可以訪問到類中定義的屬性和方法;是類的,就是大家的;
是實例的,就是這個實例自己的,通過類訪問不到;是實例的,就是個體的;
類變量,是屬于類的變量,這個類的所有實例可以共享這個變量;
實例可以動態的給自己添加或刪除一個屬性,實例.__dict__['變量名']和實例.變量名都可訪問到;也可動態添加類方法,這些會破壞封裝,不要這么做,雖語法允許,但從設計角度不好;
實例的變量會隱藏與其同名的類的變量(遮蓋),或者說是覆蓋了類變量;
?
實例屬性的查找順序:
實例使用.點來訪問屬性,會先找自己的__dict__;
如果沒有,然后通過屬性__class__找到自己的類,再去類的__dict__中找;
如果實例使用__dict__['變量名']來訪問(直接翻字典),將不會按照上面的查找順序找變量;
?
一般類變量使用全大寫來命名;
tom.__class__.__dict__等價于Person.__dict__;
?
例:
class Person:
??? age = 18?? #類變量,通常是一常量,很少用
??? def __init__(self,name):
??????? self.name = name?? #編程中,大量用的是實例變量,而不是類變量
?
# tom = Person('tom',20)?? #X
tom = Person('tom')?? #初始化、實例化
jerry = Person('jerry')
print(tom.name,tom.age)
print(jerry.name,jerry.age)
print(Person.age)
# print(Person.name)?? #X
Person.age = 30?? #在外部修改類屬性
print(tom.age,jerry.age,Person.age)
?
print(Person.__dict__)?? #__weakref__弱引用,用.點查找屬性
print(tom.__dict__)?? #每個對象保存著自己的屬性,所有對象的操作方法是一樣的,無非是數據不一樣(傳入的參數不一樣)
print(jerry.__dict__)
print(tom.__dict__['name'])
?
print(sorted(Person.__dict__.items()),end='\n')
print(sorted(tom.__dict__.items()),end='\n')
?
# print(tom.__qualname__)?? #某一對象并不擁有所有特殊屬性
print(tom.__class__.__qualname__,jerry.__class__.__qualname__)
print(isinstance(jerry,tom.__class__))
print(int.__class__)
print(Person.__class__)
print(isinstance(tom,int.__class__))
輸出:
tom 18
jerry 18
18
30 30 30
{'__module__': '__main__', 'age': 30, '__init__': <function Person.__init__ at 0x7f7b63da20d0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'tom'}
{'name': 'jerry'}
tom
[('__dict__', <attribute '__dict__' of 'Person' objects>), ('__doc__', None), ('__init__', <function Person.__init__ at 0x7f7b63da20d0>), ('__module__', '__main__'), ('__weakref__', <attribute '__weakref__' of 'Person' objects>), ('age', 30)]
[('name', 'tom')]
Person Person
True
<class 'type'>
<class 'type'>
False
?
例:
class Person:
??? age = 3
??? height = 170
??? def __init__(self,name,age=18):?? #方法中的第一個參數self,表示bound了對象(實例)
??????? self.name = name
??????? self.age = age
?
tom = Person('tom')
jerry = Person('jerry',20)
?
Person.age = 30
print(Person.age,tom.age,jerry.age)
print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
print(Person.height,tom.height,jerry.height)
Person.height += 20
print(Person.height,tom.height,jerry.height)
print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
?
tom.height = 168
print(Person.height,tom.height,jerry.height)
print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
?
jerry.height += 20
print(Person.height,tom.height,jerry.height)
print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
?
Person.weight = 70?? #動態添加(刪除)屬性,靈活之處,也可動態添加方法,這些會破壞封裝,不要這么做
print(Person.weight,tom.weight,jerry.weight)?? #先找自己的__dict__,找不到再通過__class__找類中的__dict__,類中也沒有拋異常KeyError
print(tom.__dict__['weight'])?? #X,KeyError
輸出:
30 18 20
{'__module__': '__main__', 'age': 30, 'height': 170, '__init__': <function Person.__init__ at 0x7f9f7aad40d0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'tom', 'age': 18}
{'name': 'jerry', 'age': 20}
170 170 170
190 190 190
{'__module__': '__main__', 'age': 30, 'height': 190, '__init__': <function Person.__init__ at 0x7f9f7aad40d0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'tom', 'age': 18}
{'name': 'jerry', 'age': 20}
190 168 190
{'__module__': '__main__', 'age': 30, 'height': 190, '__init__': <function Person.__init__ at 0x7f9f7aad40d0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'tom', 'age': 18, 'height': 168}
{'name': 'jerry', 'age': 20}
190 168 210
{'__module__': '__main__', 'age': 30, 'height': 190, '__init__': <function Person.__init__ at 0x7f9f7aad40d0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'tom', 'age': 18, 'height': 168}
{'name': 'jerry', 'age': 20, 'height': 210}
70 70 70
Traceback (most recent call last):
? File "/home/python/magedu/projects/cmdb/example_class_var.py", line 29, in <module>
??? print(tom.__dict__['weight'])
KeyError: 'weight'
?
?
?
不是類裝飾器;
需求:為一個類通過裝飾,增加一些類屬性;
用于老項目,不動類定義,通過裝飾器動態的添加類屬性;
?
def setnameproperty(name):
??? def wrapper(cls):
??????? cls.NAME = name
??????? return cls
??? return wrapper
?
@setnameproperty('MYCLASS')? ?#MyClass = setnameproperty('MYCLASS')(MyClass)
class MyClass:
??? pass
?
print(MyClass.__dict__)
輸出:
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None, 'NAME': 'MYCLASS'}
?
?
?
類方法,用裝飾器@classmethod,在定義時,第1個參數留給類本身,如def clsmtd(cls),與類bound(類似對象方法,第1個參數self,與實例bound);
類中普通方法,def bar(),雖語法允許,不推薦使用,若非要不帶參數,用靜態方法替代;
靜態方法,用裝飾器@staticmethod;
?
python中類方法,相當于其它語言的靜態方法;java、c++中的靜態方法指的是python中的類方法;
?
類方法,如datetime.datetime(...)創建時間對象;
靜態方法,從概念上歸類管轄,實際使用可以與類沒有關系,很少用;
?
__init__()等方法,這些本身都是類的屬性,第一個參數必須是self,而self必須指向一個對象,即類必須實例化后,由實例來調用這個方法;
?
普通方法:
MyClass.bar(),先查看MyClass.__dict__,這個方法只是被MyClass這個名詞空間管理的一個普通的方法,bar是MyClass的一個屬性而已,由于bar在定義時沒有指定self,這樣即沒有完成與實例對象的綁定,不能用a.bar()或MyClass().bar()調用,這種雖語法對,但沒人這么用,禁止這樣定義def bar();
?
類方法:
在類定義中,用@classmethod裝飾器修飾的方法;
pycharm中,定義時會自動補全cls參數,必須至少有一個參數,且第一個參數留給了cls,cls指代調用者即對象自身;
cls這個標識符可以是任意合法名稱,但是為了易讀,請不要修改;
通過cls可直接操作類的屬性,無法通過cls操作類的實例;
類方法,類似c++、java中的靜態方法;
?
靜態方法:
在類定義中用@staticmethod裝飾器修飾的方法;
調用時,不會隱式的傳入參數;
靜態方法,只是表明這個方法屬于類這個名詞空間,函數歸在一起,方便組織管理;
?
注:
實例可調用類中定義的方法(包括類方法、靜態方法),靜態方法和類方法需要找到實例的類;
實例不可調用類中的普通方法,如a.bar(),解釋器會自動將a傳到bar(a)內,而bar在定義時沒有self,即沒有綁定實例;
?
例:
class MyClass:
??? XXX = 'xxx'
??? def __init__(self):
??????? print('init')
?
??? def foo(self):
??????? print('foo')
?
??? def bar():
??????? print('bar')
?
??? @classmethod
??? def clsmtd(cls):?? #類方法,cls為類本身,或是實例的類
??????? print('{},xxx={}'.format(cls.__name__,cls.XXX))
?
??? @staticmethod
??? def staticmtd():
??? # def staticmtd(x):
??????? print('static')
?
a = MyClass()
a.foo()
MyClass.bar()?? #V,可以這樣用,bar是在MyClass類下定義的
# a.bar()?? #X,foo和bar都是function,bar中形參沒有self,為類中的普通函數,這樣調用會自動把a作為形參傳入到bar(a)會出錯
print(MyClass.__dict__)
?
MyClass.clsmtd()?? #同a.foo(),會將類本身MyClass傳入clsmtd(MyClass)類方法的第一個參數
a.clsmtd()?? #是類的,就是大家的,所以實例可以用,相當于a.__class__.clsmtd()
?
MyClass.staticmtd()?? #V
a.staticmtd()?? #是類的,就是大家的
# a.staticmtd(4)
?
輸出:
init
foo
bar
{'__module__': '__main__', 'XXX': 'xxx', '__init__': <function MyClass.__init__ at 0x7f97c11f30d0>, 'foo': <function MyClass.foo at 0x7f97c11f3158>, 'bar': <function MyClass.bar at 0x7f97c11f3488>, 'clsmtd': <classmethod object at 0x7f97c11f5c18>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None}
MyClass,xxx=xxx
MyClass,xxx=xxx
static
static
?
例:
class Person:
??? @classmethod
??? def cls_method(cls):
??????? print('class = {0.__name__} ({0})'.format(cls))
??????? cls.HEIGHT = 170?? #可操作類的屬性,不能操作實例的屬性
?
??? @staticmethod
??? def static_method():
??????? print(Person.HEIGHT)
?
Person.cls_method()
print(Person.__dict__)
Person.static_method()
輸出:
class = Person (<class '__main__.Person'>)
{'__module__': '__main__', 'cls_method': <classmethod object at 0x7fdfe7ec4710>, 'static_method': <staticmethod object at 0x7fdfe7ec9400>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'HEIGHT': 170}
170
?
?
?
private,私有屬性,雙下劃線開頭的屬性名;
私有變量的本質:類定義的時候,如果聲明一個實例變量使用雙下劃線開頭,python解釋器會將其改名,轉換名稱為_類名__變更名,所以在外部用原來的名字(__age)訪問不到了,但仍可通過__dict__訪問或修改;其它語言不是這樣,是確確實實看不到,python中仍然可在外部通過_Person__age訪問或修改;
?
保護變量:
在變更名前加一個下劃線,查看__dict__,解釋器不做任何特殊處理,這只是開發者共同的約定,看見這種變量,就如同私有變量,不要直接使用;
?
私有方法:
雙下劃線開頭的方法,解釋器會改名,改名策略和私有變量相同,_類名__方法名;
單下劃線的方法,只是開發者之間的約定,解釋器不作任何改變;
方法變量都在類的__dict__中可找到;
?
私有成員的總結:
在python中使用_或__來標識一個成員被保護或被私有化隱藏起來,但是不管使用什么樣的訪問控制,都不能真正的阻止用戶修改類的成員,python中沒有絕對的安全的保護成員或私有成員;
因此前導的下劃線只是一種警告或提醒,請遵守這個約定,除非真有必要,不要修改或者使用保護成員或私有成員;
以上是在類中使用_或__,如果是在函數中使用則不起作用;
?
public、protect、private,其它語言的訪問控制,而python中的訪問控制只是假象;
?
例:
class Person:
??? def __init__(self,name,age=18):
??????? self.name = name
??????? self.age = age
?
??? def growup(self,incr):
?????? ?if 0 < incr < 150:?? #控制邏輯
??????????? self.age += incr
?
tom = Person('tom')
tom.growup(20)
tom.age = 160?? #未用私有屬性,會繞過控制邏輯,外部可見,外部可定義和修改,越過我們定義的范圍
print(tom.age)
輸出:
160
?
例:
class Person:
??? def __init__(self,name,age=18):
??????? # self.name = name
??????? self._name = name
??????? # self.age = age
??????? self.__age = age?? #對象字典中的key表現為_Person__age,自動對應的
?
??? def growup(self,incr):
??????? if 0 < incr < 150:
??????????? # self.age += incr
??????????? self.__age += incr
?
??? def getage(self):
??????? return self.__age
?
tom = Person('tom')
tom.growup(2)
# tom.age = 160
# print(tom.age)
# print(tom.__age)?? #X,不能直接訪問私有屬性,報AttributeError: 'Person' object has no attribute '__age'
print(tom.getage())?? #通過方法訪問私有屬性,沒多大用處
?
print(Person.__dict__)
print(tom.__dict__)
?
tom._Person__age = 200?? #破壞封裝
print(tom.getage())
?
tom.__age = 300?? #破壞封裝,重新定義的__age,與對象字典的_Person__age不是同一個key,不會被覆蓋
print(tom.getage())
print(tom.__dict__)
print(tom.__age)
?
輸出:
20
{'__module__': '__main__', '__init__': <function Person.__init__ at 0x7fde68b150d0>, 'growup': <function Person.growup at 0x7fde68b15158>, 'getage': <function Person.getage at 0x7fde68b15488>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'_name': 'tom', '_Person__age': 20}
200
200
{'_name': 'tom', '_Person__age': 200, '__age': 300}
300
tom
{'_name': 'tom', '_Person__age': 200, '__age': 300}
?
?
?
可通過修改或者替換類的成員;
使用者調用的方式沒有改變,但類提供的功能可能已經改變了;
適用于上線后的緊急或臨時修復上,一般下次變更時會合并到代碼中;
?
monkey patch,猴子補丁,在運行時對屬性進行動態替換;
黑魔法,慎用;
?
類方法中的名字,get*讀,set*寫,慣例;
?
例:
使用monkey patch,替換getscore方法,返回模擬的數據;
?
test2.py
class Person:
??? def __init__(self,chinese,english,history):
??????? self.chinese = chinese
??????? self.english = english
??????? self.history = history
?
??? def getscore(self):
??????? return self.chinese,self.english,self.history
?
test3.py
def getscore(self):
??? return dict(chinese=self.chinese,english=self.english,history=self.history)
?
test1.py
from test2 import Person
from test3 import getscore
?
def monkeypatch5Person():
??? Person.getscore = getscore
?
stu1 = Person(80,90,88)
print(stu1.getscore())
?
monkeypatch5Person()
?
stu2 = Person(70,80,90)
print(stu2.getscore())
輸出:
(80, 90, 88)
{'chinese': 70, 'english': 80, 'history': 90}
?
?
?
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。