Python :: 屬性與方法
文章推薦指數: 80 %
類別方法
OPENHOME.CC
Python
|起步走
Hello,Python
簡介模組
IO/格式/編碼
|內建型態
數值
字串
清單
集合
字典
tuple
|基本運算
變數
算術運算
比較、指定、邏輯運算
位元運算
|流程語法
if分支判斷
while迴圈、for迭代
forComprehension
初試match/case
|函式入門
定義函式
一級函式、lambda運算式
初探變數範圍
yield產生器
|封裝
類別入門
屬性與方法
屬性名稱空間
特殊方法
callable物件
|繼承
共同行為與isa
使用enum列舉
多重繼承與Mixin
|例外處理
使用try、except
例外繼承架構
raise例外
使用else、finally
使用withas
使用assert
|模組/套件
管理模組名稱
模組路徑
使用套件
|metaprogramming
__slots__、__abstractmethods__、__init_subclass__
__getattribute__、__getattr__、__setattr__、__delattr__
裝飾器
描述器
type類別
metaclass
super與mro
GitHub
Twitter
Facebook
LinkedIn
2DDesigns
3DDesigns
Tags
BuiltwithbyHugo
HOME>
Python>
封裝>
屬性與方法
定義內部屬性
定義外部屬性
綁定與未綁定方法
靜態方法
類別方法
encapsulation
objectoriented
uniformaccess
namespace
callable
static
屬性與方法
April24,2022
在〈類別入門〉中,Account類別擁有name、number與balance三個屬性可供存取,雖然你設計了deposit、withdraw方法,希望使用者想變更Account物件的狀態時,都透過這些方法,然而,可能會有人如下誤用:
acct=Account('Justin','123-4567',1000)
acct.balance=1000000
定義內部屬性
如果想避免使用者這類誤用,可以使用self.__xxx的方式定義內部值域。
例如:
classAccount:
def__init__(self,name,number,balance):
self.__name=name
self.__number=number
self.__balance=balance
defdeposit(self,amount):
ifamount<=0:
print('存款金額不得為負')
else:
self.__balance+=amount
defwithdraw(self,amount):
ifamount>self.__balance:
print('餘額不足')
else:
self.__balance-=amount
def__str__(self):
returnf"Account('{self.__name}','{self.__number}',{self.__balance})"
在Account類別的方法定義中,可以使用self.__name、self.__number、self.__balance來存取屬性;然而,若使用者建立Account實例並指定給acct,不能使用acct.__name、acct.__number、acct.__balance來進行屬性的存取(子類別中定義的方法也不能以self.__name、self.__number、self.__balance來存取屬性),這會引發AttributeError,因為屬性若使用__xxx這樣的名稱,會自動轉換為「_類別名稱__xxx」。
Python沒有完全阻止存取,只要在原本的屬性名稱前加上_類別名稱,仍舊可以存取到名稱為__開頭的屬性:
acct=Account('Justin','123-4567',1000)
print(acct._Account__name)
acct._Account__balance=1
然而並不建議這麼做,__xxx名稱的屬性,慣例上是作為類別定義時,內部相關流程操作之用,外界最好不要知道其存在,更別說是操作了,如果真想這麼做,最好是清楚地知道自己在做些什麼。
兩個底線__開頭的屬性,基本上表示私有(private),只在定義的類別中使用;如果屬性想讓子類別存取,Python的慣例會使用一個底線_,代表它是一個受保護的(protected)屬性,這僅僅只是一個提示,python直譯器不會做任何變換,只是提示使用者存取該屬性必須慎重。
定義外部屬性
之前使用了__xxx這樣的格式來定義了內部屬性,不過留下了一個問題,若只是想取得帳戶名稱、帳號、餘額等資訊,以便在相關使用者介面上顯示,那該怎麼辦呢?以acct._Account__name這樣的方式是不建議的,那還能用什麼方式?
基本上,可以直接定義一些方法來傳回self.__name、self.__number這些內部屬性的值,例如:
classAccount:
一些程式碼…略
defname(self):
returnself.__name
defnumber(self):
returnself.__number
defbalance(self):
returnself.__balance
這麼一來,使用者就可以使用acct.name()、acct.number()這樣的方式來取得值,不過,針對這種情況,可以考慮在這類方法上加註@property,例如:
classAccount:
def__init__(self,name,number,balance):
self.__name=name
self.__number=number
self.__balance=balance
@property
defname(self):
returnself.__name
@property
defnumber(self):
returnself.__number
@property
defbalance(self):
returnself.__balance
其他程式碼同前一範例,故略…
acct=Account('Justin','123-4567',1000)
acct.deposit(500)
acct.withdraw(200)
print('帳戶名稱:',acct.name)
print('帳戶號碼:',acct.number)
print('帳戶餘額:',acct.balance)
現在使用者可以使用acct.name、acct.number、acct.balance這樣的形式取得值,然而,就目前的程式碼撰寫,無法直接使用acct.balance=10000這樣的形式來設定屬性值,因為@property只允許acct.balance這樣的形式取值。
如果在程式設計的一開始,沒有使用self.__balance的方式,而是以self.balance定義內部屬性,而使用者也使用了acct.balance取得值,後來進一步考慮要避免被誤用,想修改為self.__balance定義內部屬性,這時就可以像上面的範例,定義一個方法並加註@property,如此一來,使用者原本的程式碼也不會受到影響,這是固定存取原則(Uniformaccessprinciple)的實現。
如果這個範例,想進一步提供acct.balance=10000這樣的形式,可以使用@name.setter、@number.setter、@balance.setter標註對應的方法。
例如:
classAccount:
略…
@name.setter
defname(self,name):
#可實作一些設值時的條件控制
self.__name=name
@number.setter
defnumber(self,number):
#可實作一些設值時的條件控制
self.__number=number
@balance.setter
defbalance(self,balance):
#可實作一些設值時的條件控制
self.__balance=balance
略…
被@property標註的xxx取值方法(Getter),可以使用@xxx.setter標註對應的設值方法(Setter),使用@xxx.deleter來標註對應的刪除值之方法。
取值方法傳回值可以是即時運算的結果,而設值方法中,必要時可以使用流程語法等來實作一些存取控制。
綁定與未綁定方法
定義在類別中的方法,本質上也是函式,以目前定義的Account類別為例,執行type(Account.deposit)的話,會得到
實際上,也可以如下取得相同效果:
acct=Account('Justin','123-4567',1000)
Account.deposit(acct,500)
不過,若試著將acct.deposit或acct.withdraw指定給一個變數,會發現變數實際上參考著一個綁定方法:
>>>acct=Account('Justin','123-4567',1000)
>>>deposit=acct.deposit
>>>withdraw=acct.withdraw
>>>deposit
在Python中,實作了__call__方法的物件,行為上可以像函式進行呼叫,稱為callable物件,其實到目前為止,你已經用過不少callable物件,例如,類別本身就是個callable物件。
使用acct.deposit(500)的方式來呼叫方法時,acct參考的物件,實際上就會傳給deposit方法的第一個參數,相對地,如果在類別中定義了一個方法,沒有任何參數會怎樣呢?
>>>classSome:
...defnothing():
...print('nothing')
...
>>>s=Some()
>>>s.nothing()
Traceback(mostrecentcalllast):
File"
有沒有辦法取得綁定方法綁定的物件呢?雖然不鼓勵,不過確實可以透過綁定方法的__self__屬性來取得。
相對於綁定方法,像這樣定義在類別中,沒有定義self參數的方法,稱為未綁定方法(Unboundmethod),這類方法,充其量只是將類別名稱作為一種名稱空間,可以透過類別名稱來呼叫它,或取得函式物件進行呼叫:
>>>Some.nothing()
nothing
>>>nothing=Some.nothing
>>>nothing()
nothing
>>>
靜態方法
現在假設,想在Account類別增加一個default函式,以便建立預設帳戶,只需要指定名稱與帳號,開戶時餘額預設為100:
classAccount:
…略
defdefault(name,number):
returnAccount(name,number,100)
當然,這個需求也可以在__init__上使用預設引數來達成,這邊只是為了示範,在更真實的情境中,可能是建構實例前,有個複雜的流程,像是收集、確認開戶者的其他資格等,因而有了default這類函式存在的需求。
你原本的用意,是希望default函式是以Account類別作為名稱空間,因為它與建立帳戶有關,而使用者應該要以Account.default('Monica','765-4321')這樣的方式來呼叫它,然而,若使用者如下誤用,正好也能夠執行:
acct=Account('Justin','123-4567',1000)
#顯示Account(Account(Justin,123-4567,1000),1000,100)
print(acct.default(1000))
就這個例子來說,acct參考的物件,傳給了default方法的第一個參數name,而執行過程正好也沒有引發錯誤,只不過顯示了怪異的結果。
若在定義類別時,希望某方法不被拿來作為綁定方法,可以使用@staticmethod加以標註。
例如:
classAccount:
…略
@staticmethod
defdefault(name,number):
returnAccount(name,number,100)
這麼一來,可以使用Account.default('Monica','765-4321')這樣的方式來呼叫它,就算使用者透過類別的實例來呼叫它,像是acct.default('Monica','765-4321'),acct也不會被傳入作為default的第一個參數。
雖然可以透過實例來呼叫@staticmethod標註的方法,但建議透過類別名稱來呼叫,明確地讓類別名稱作為靜態方法的名稱空間。
類別方法
來仔細看看上面的例子,default方法中寫死了Account這個名稱,萬一要修改類別名稱的話,還要記得修改default中的類別名稱,我們可以讓default的實作更有彈性。
首先得知道的是,在Python中定義的類別,也會產生對應的物件,這個物件會是type的實例。
例如:
>>>classSome:
...pass
...
>>>Some
因此,只要能在先前的default方法中,取得目前所在類別的type實例,就可以不用寫死類別名稱了,對於這個需求,可以在default方法上標註@classmethod。
例如:
classAccount:
…略
@classmethod
defdefault(cls,name:str,number:str):
returncls(name,number,100)
類別中的方法若標註了@classmethod,第一個參數一定是接受所在類別的type實例,因此,在default方法中,就可以使用第一個參數來建構物件。
延伸文章資訊
- 19. Class(類別) — Python 3.10.7 說明文件
Class 實例也可以有一些(由其class 所定義的)method(方法),用於修改該實例 ... Python 的class 提供了所有物件導向程式設計(Object Oriented Pr...
- 2Python 速查手冊- 6.5 static 方法與類別方法 - 程式語言教學誌
本篇文章介紹Python 類別的static 方法與類別方法。 ... static 方法(static method) 與類別方法(class method) 都是透過類別(class) 名稱...
- 3Python OOP物件導向設計的類型方法(Instance, Class, Static ...
類別方法(Class Method)可以存取類別,可以呼叫類別本身,用來製作替代的建構子。而靜態方法(Static Method)無法存取物件與類別,它獨立於物件或類別以外 ...
- 4[Python物件導向]解析Python物件導向設計的3種類型方法 ...
- 5第15 章物件與類別 - Python
▸ 以Circle 為例,在類別中加入__init__() 方法, 用來設定圓心位置及半徑的初始值:. circle.py. class Circle: def __init__(self, c...