新聞中心
1. class語(yǔ)句
類(lèi)通常是由函數(shù)、變量和屬性組成的集合。使用class語(yǔ)句可以定義類(lèi),例如:
class Account(object):
num_accounts = 0
def __init__(self, name, balance):
self.name = name
self.balance = balance
Account.num_accounts += 1
def __del__(self):
Account.num_accounts -= 1
def deposit(self, amt):
self.balance = self.balance + amt
def withdraw(self, amt):
self.balance = self.balance - amt
def inquiry(self):
return self.balance
在類(lèi)主體執(zhí)行期間創(chuàng)建的值放在類(lèi)對(duì)象中,這個(gè)對(duì)象充當(dāng)著命名空間,例如:
Account.num_accunts
Account.__init__
Account.__del__
Account.deposit
Account.withdraw
Account.inquiry
需要注意的是,class語(yǔ)句本身并不創(chuàng)建該類(lèi)的任何類(lèi)型。類(lèi)僅設(shè)置將在以后創(chuàng)建的所有實(shí)例都使用的屬性。類(lèi)中定義的函數(shù)稱(chēng)為實(shí)例方法。類(lèi)的實(shí)例作為第一個(gè)參數(shù)傳遞,根據(jù)約定,這個(gè)參數(shù)稱(chēng)為self,但所有合法的標(biāo)識(shí)符都可以使用。類(lèi)變量是可在類(lèi)的所有實(shí)例之間共享的值。比如上例的num_accounts變量用于跟蹤存在多少個(gè)Account實(shí)例。
?
2. 類(lèi)實(shí)例
類(lèi)的實(shí)例是以函數(shù)形式調(diào)用類(lèi)對(duì)象來(lái)創(chuàng)建的。這種方法將創(chuàng)建一個(gè)新實(shí)例,而后將該實(shí)例傳遞給類(lèi)的__init__()方法。__init__()方法的參數(shù)包括新創(chuàng)建的實(shí)例self和在調(diào)用類(lèi)對(duì)象時(shí)提供的參數(shù)。例如:
a = Account("Guido", 1000.00)
b = Account("Bill", 10.00)
通過(guò)將屬性分配給self來(lái)將其保存到實(shí)例中。例如self.name = name表示將name屬性保存在衫例中。使用"."運(yùn)算符可以訪(fǎng)問(wèn)這些屬性以及類(lèi)屬性,例如:
a.deposit(100.00)
b.withdraw(50.00)
name = a.name
盡管類(lèi)會(huì)定義命名空間,但它們不會(huì)為在方法體內(nèi)使用的名稱(chēng)限定范圍。所以在實(shí)現(xiàn)類(lèi)時(shí),對(duì)屬性和方法的引用必須是完全限定的。比如之前的例子中使用的是self.balance而非balance。如果希望從一個(gè)方法中調(diào)用另一個(gè)方法,也可以采用這種方式,例如:
class Foo(object):
def bar(self):
print("bar!")
def spam(self):
bar(self) # 錯(cuò)誤,拋出NameError異常
self.bar()
Foo.bar(self)
3. 繼承
繼承是一種創(chuàng)建新類(lèi)的機(jī)制。原始類(lèi)稱(chēng)為基類(lèi)或超類(lèi)。新類(lèi)稱(chēng)為派生類(lèi)或子類(lèi)。通過(guò)繼承創(chuàng)建類(lèi)時(shí),所創(chuàng)建的類(lèi)將“繼承”其基類(lèi)定義的屬性。派生類(lèi)可以重新定義屬性并添加自己的屬性。
在class語(yǔ)句中使用以逗號(hào)分隔的基類(lèi)名稱(chēng)列表來(lái)指定繼承。如果沒(méi)有有效的基類(lèi),將繼承object。繼承通常會(huì)重新定義現(xiàn)有方法的行為,例如:
import random
class EvilAccount(Account):
def inquiry(self):
if andom.randint(0, 4) == 1:
return self.balance * 1.10
else:
return self.balance
c = EvilAccount("George", 1000.00)
c.deposit(10.0)
available = c.inquiry()
如果搜索一個(gè)屬性時(shí)未在實(shí)例或?qū)嵗念?lèi)中找到匹配項(xiàng),搜索將會(huì)在基類(lèi)上進(jìn)行。這個(gè)過(guò)程會(huì)一直繼續(xù)下去,直到?jīng)]有更多的基類(lèi)可供搜索。子類(lèi)可以定義自己的__init__()方法。因此,要由派生類(lèi)調(diào)用基類(lèi)的__init__()方法來(lái)對(duì)它們進(jìn)行恰當(dāng)?shù)某跏蓟H绻?lèi)未定義__init__(),就可以忽略這一步。如果不知道基類(lèi)是否定義了__init__(),可在不提供任何參數(shù)的情況下調(diào)用它,因?yàn)槭冀K存在一個(gè)不執(zhí)行任何操作的默認(rèn)__init__()實(shí)現(xiàn)。例如:
class EvilAccount(Account):
def __init__(self, name, balance, evilfactor):
Account.__init__(self, name, balance)
self.evilfactor = evilfactor
def inquiry(self):
if random.randint(0, 4) == 1:
return self.balance * self.evilfactor
else:
return self.balance
有時(shí),派生類(lèi)重新實(shí)現(xiàn)了方法,但是還想調(diào)用原始的實(shí)現(xiàn),可以將實(shí)例self作為第一個(gè)參數(shù)傳遞,例如:
class MoreEvilAccount(EvilAccount):
def deposit(self, amount):
self.withdraw(5.00)
EvilAccount.deposit(self, amount)
但是這種寫(xiě)法容易引起一些混淆,可以使用另一種方案,用super()函數(shù),例如:
class MoreEvilAccount(EvilAccount):
def deposit(self, amount):
self.withdraw(5.00)
super().deposit(amount)
Python支持多重繼承,通過(guò)讓一個(gè)類(lèi)列出多個(gè)基類(lèi)即可指定多重繼承,例如:
class DepositCharge(object):
fee = 5.00
def deposit_fee(self):
print(self.fee)
class WithdrawCharge(object):
fee = 2.50
def withdraw_fee(self):
print(self.fee)
class MostEvilAccount(EvilAccount, DepositCharge, WithdrawCharge):
def deposit(self, amt):
self.deposit_fee()
super().deposit(amt)
def withdraw(self, amt):
self.withdraw_fee()
super().withdraw(amt)
withdraw_fee()實(shí)際并未使用在自己的類(lèi)中初始化的fee值。屬性fee是在兩個(gè)不同的基類(lèi)中定義的類(lèi)變量,程序使用了其中一個(gè)。要找到使用了多重繼承的屬性,可以在列表中對(duì)所有基類(lèi)按從“最特殊”的類(lèi)到“最不特殊”的類(lèi)。而后在搜索屬性時(shí),就會(huì)按這個(gè)順序搜索列表,直至找到該屬性的第一個(gè)定義。對(duì)于任何給定的類(lèi),通過(guò)打印它的__mro__屬性即可查看基類(lèi)的順序。
4. 靜態(tài)方法和類(lèi)方法
靜態(tài)方法是一種普通函數(shù),就位于類(lèi)定義的命名空間中。要定義靜態(tài)方法,可使用@staticmethod裝飾器,例如:
class Foo(object):
@staticmethod
def add(x, y):
return x + y
要調(diào)用靜態(tài)方法,只需用類(lèi)名作為它的前綴,例如:
x = Foo.add(3, 4)
類(lèi)方法是將類(lèi)本身作為對(duì)象進(jìn)行操作的方法。類(lèi)方法使用@classmethod裝飾器定義,根據(jù)約定,類(lèi)是作為第一個(gè)參數(shù)(名為cls)傳遞的,例如:
class Times(object):
factor = 1
@classmethod
def mul(cls, x):
return cls.factor * x
class TwoTimes(Times):
factor = 2
x = TwoTimes.mul(4)
5. 特性
特性是一種特殊的屬性,訪(fǎng)問(wèn)它時(shí)會(huì)計(jì)算它的值,例如:
class Circle(object):
def __init__(self, radius):
self.radius = radius
@property
def area(self):
return math.pi * self.radius ** 2
@property
def perimeter(self):
return 2 * math.pi * self.radius
在這個(gè)例子中,Circle實(shí)例存儲(chǔ)了一個(gè)實(shí)例變量c.radius。c.area和c.perimeter是根據(jù)該值計(jì)算得來(lái)的。@property裝飾器支持以簡(jiǎn)單屬性的形式訪(fǎng)問(wèn)后面的方法,無(wú)需添加額外的()來(lái)調(diào)用該方法。方法本身是作為一類(lèi)特性被隱式處理的,當(dāng)創(chuàng)建一個(gè)實(shí)例然后訪(fǎng)問(wèn)實(shí)例的方法時(shí),不會(huì)返回原始函數(shù)對(duì)象,會(huì)得到綁定方法。綁定方法是一個(gè)對(duì)象,表示將在對(duì)象中調(diào)用()運(yùn)算符時(shí)執(zhí)行的方法調(diào)用。這種綁定方法對(duì)象是由在后臺(tái)執(zhí)行的特性函數(shù)靜默創(chuàng)建的。使用@staticmethod和@classmethod定義靜態(tài)方法和類(lèi)方法時(shí),實(shí)際上就指定了使用不同的特性函數(shù)。
特性還可以攔截操作,以設(shè)置和刪除屬性。這是通過(guò)向特性附加其他setter和deleter方法來(lái)實(shí)現(xiàn)的,例如:
class Foo(object):
def __init__(self, name):
self.__name = name
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError("Must be a string!")
self.__name = value
@name.deleter
def name(self):
raise TypeError("Can't delete name")
f = Foo("Guido")
n = f.name
f.name = "Monty" # 調(diào)用setter
f.name = 45 # 調(diào)用setter(TypeError)
del f.name
6. 描述符
使用特性后,對(duì)屬性的訪(fǎng)問(wèn)將由一系列用戶(hù)定義的get、set和delete函數(shù)控制。這種屬性控制方式可以通過(guò)描述符對(duì)象進(jìn)一步推廣。描述符就是一個(gè)表示屬性值的對(duì)象,通過(guò)實(shí)現(xiàn)一個(gè)或多個(gè)特殊的__get__()、__set__()和__delete__()方法,可以將描述符與屬性訪(fǎng)問(wèn)機(jī)制掛鉤,例如:
class TypedProperty(object):
def __init__(self, name, type, default=None):
self.name = "_" + name
self.type = type
self.default = default if default else type()
def __get__(self, instance, cls):
return getattr(instance, self.name, self.default)
def __set__(self, instance, value):
if not isinstance(value, self.type):
raise TypeError("Must be a %s" % self.type)
setattr(instance, self.name, value)
def __delete__(self, instance):
raise AttributeError("Can't delete attribute")
class Foo(object):
name = TypedProperty("name", str)
num = TypedProperty("num", int, 42)
在這個(gè)例子中,類(lèi)TypedProperty定義了一個(gè)描述符,分配屬性時(shí)它將進(jìn)行類(lèi)型檢查,例如:
f = Foo()
a = f.name # 隱式調(diào)用Foo.name.__get__(f.Foo)
f.name = "Guido" # 調(diào)用Foo.name.__set__(f, "Guido")
del f.name # 調(diào)用Foo.name.__delete__(f)
7. 數(shù)據(jù)封裝和私有屬性
默認(rèn)情況下,類(lèi)的所有屬性和方法都是“公共的”。這意味著對(duì)它們的訪(fǎng)問(wèn)沒(méi)有任何限制。在基類(lèi)中的所有內(nèi)容都會(huì)被 派生類(lèi)繼承,并可從派生類(lèi)內(nèi)進(jìn)行訪(fǎng)問(wèn)。這可能導(dǎo)致在派生類(lèi)中定義的對(duì)象與在基類(lèi)中定義的對(duì)象之間發(fā)生命名空間沖突,為了解決該問(wèn)題,類(lèi)中所有以雙下劃線(xiàn)開(kāi)頭的名稱(chēng)都會(huì)變成具有_類(lèi)名__Foo形式的新名稱(chēng)。例如:
class A(object):
def __init__(self):
self.__X = 3 # 變?yōu)閟elf._A__X
def __spam(self): # 變成_A__spam()
pass
def bar(self):
self.__spam() # 只調(diào)用A.__spam()
class B(A):
def __init__(self):
A.__init__(self)
self.__X = 37 # 變?yōu)閟elf._B__X
def __spam(self): # 變?yōu)開(kāi)B__spam()
pass
這種方案似乎隱藏了數(shù)據(jù),但并沒(méi)有嚴(yán)格的機(jī)制來(lái)實(shí)際阻止對(duì)類(lèi)的“私有”屬性進(jìn)行訪(fǎng)問(wèn)。盡管這種變形似乎是一個(gè)額外的處理步驟,但變形過(guò)程實(shí)際上只在定義類(lèi)時(shí)發(fā)生一次。而且,名稱(chēng)變形不會(huì)在getattr()、hasattr()、setattr()或delattr()等函數(shù)中發(fā)生,因?yàn)樵谶@些函數(shù)中,屬性名稱(chēng)指定為字符串。對(duì)于這些函數(shù),需要顯示使用變形名稱(chēng)。
8. 對(duì)象表示和屬性綁定
在類(lèi)的內(nèi)部,實(shí)例是使用字典來(lái)實(shí)現(xiàn)的,可以用實(shí)例的__dict__屬性的形式訪(fǎng)問(wèn)該字典。這個(gè)字典包含的數(shù)據(jù)對(duì)每個(gè)實(shí)例而言都是唯一的。對(duì)實(shí)例的修改始終會(huì)反映到局部__dict__屬性中。同樣,如果直接對(duì)__dict__進(jìn)行修改,所做的修改也會(huì)反映在該屬性中。
實(shí)例被特殊屬性__class__鏈接回它們的類(lèi)。在特殊屬性__base__中將類(lèi)鏈接到它們的基類(lèi)。只要使用obj.name = value設(shè)置屬性,就會(huì)調(diào)用特殊方法obj.__setattr__("name", value)。如果使用del obj.name刪除了一個(gè)屬性,就會(huì)調(diào)用特殊方法obj.__delattr__("name")。
來(lái)查找屬性時(shí),將調(diào)用特殊方法obj.__getattrribute__("name")。如果搜索過(guò)程失敗,最終會(huì)嘗試調(diào)用類(lèi)的__getattr__()方法(如果已定義)來(lái)查找該屬性。如果這也失敗,就會(huì)拋出AttributeError異常。
9. 運(yùn)算符重載
用戶(hù)可以定義Python的所有內(nèi)置運(yùn)算符,比如,如果希望向Python添加一種新的數(shù)字類(lèi)型,可以定義一個(gè)類(lèi)并在該類(lèi)中定義__add__(),例如:
class complex(object):
def __init__(self, real, imag=0):
self.real = float(real)
self.imag = float(imag)
def __repr__(self):
return "Complex(%s, %s)" % (self.real, self.imag)
def __str__(self):
return "(%g+%gj)" % (self.real, self.imag)
def __add__(self, other):
return Complex(self.real + other.real, self.imag + other.imag)
def __sub__(self, other):
return Complex(self.real - other.real, self.imag - other.imag)
在這個(gè)例子中,__repr__()方法創(chuàng)建一個(gè)字符串,可以計(jì)算該字符串來(lái)重新創(chuàng)建對(duì)象。__str__()方法創(chuàng)建具有良好輸出格式的字符串。__add__()和__sub__()實(shí)現(xiàn)數(shù)學(xué)運(yùn)算。
?
10. 抽象基類(lèi)
要定義抽象基類(lèi),需要使用abc模塊。該模塊定義一個(gè)元類(lèi)(ABCMeta)和一組裝飾器,可以按如下方式使用:
from abc import ABCMeta, abstractmethod, abstractproperty
class Foo:
__metaclass__ = ABCMeta
@abstractmethod
def spam(self, a, b):
pass
@abstractproperty
def name(self):
pass
要定義抽象類(lèi),需要將其元類(lèi)如上所示設(shè)置為ABCMeta。因?yàn)槌橄箢?lèi)的實(shí)現(xiàn)離不開(kāi)元類(lèi)。在抽象類(lèi)中,@abstractmethod和@abstractproperty裝飾器指定Foo的子類(lèi)必須實(shí)現(xiàn)一個(gè)方法或特性。抽象類(lèi)不能直接實(shí)例化。這一限制也適用于派生類(lèi),如果派生類(lèi)沒(méi)有實(shí)現(xiàn)一個(gè)或多個(gè)抽象方法,那么嘗試創(chuàng)建派生類(lèi)實(shí)例將會(huì)失敗。
抽象基類(lèi)支持對(duì)已經(jīng)存在的類(lèi)進(jìn)行注冊(cè),使其屬于該基類(lèi)。這是用register()方法完成的,例如:
class Grok(object):
def spam(self, a, b):
print("Grok.spam")
Foo.register(Grok)
10. 元類(lèi)
在Python中定義類(lèi)時(shí),類(lèi)定義本身將成為一個(gè)對(duì)象。例如:
class Foo(object): pass
isinstance(Foo, object) # True
類(lèi)對(duì)象的這種創(chuàng)建方式是由一種名為元類(lèi)的特殊對(duì)象控制的。即元類(lèi)就是知道如何創(chuàng)建和管理類(lèi)的對(duì)象。如果查看Foo的類(lèi)型,將會(huì)發(fā)現(xiàn)它的類(lèi)型為type。
使用class語(yǔ)句定義新類(lèi)時(shí),類(lèi)主體將作為其自己的私有字典內(nèi)的一系列語(yǔ)句來(lái)執(zhí)行。語(yǔ)句的執(zhí)行與正常代碼執(zhí)行過(guò)程相同,只是會(huì)在私有成員上發(fā)生名稱(chēng)變形。最后,類(lèi)的名稱(chēng)、基類(lèi)列表和字典將傳遞給元類(lèi)的解構(gòu)函數(shù),以創(chuàng)建相應(yīng)的類(lèi)對(duì)象。類(lèi)創(chuàng)建的最后一步,也就是調(diào)用元類(lèi)type()的步驟,可以自定義。
類(lèi)可以顯式地指定其元類(lèi),這通過(guò)在基類(lèi)元組中提供metaclass關(guān)鍵字參數(shù)來(lái)實(shí)現(xiàn),例如:
class Foo(metaclass=type)
__metaclass__ = type
...
如果沒(méi)有顯示指定元類(lèi), class語(yǔ)句將檢查基類(lèi)元組中的第一個(gè)條目。在這種情況下,元類(lèi)與第一個(gè)基類(lèi)的類(lèi)型相同。如果沒(méi)有指定基類(lèi),class語(yǔ)句將檢查全局變量__metaclass__是否存在。如果找到了該變量,將使用它來(lái)創(chuàng)建類(lèi)。如果沒(méi)有找到任何__metaclass__值,Python將使用默認(rèn)的元類(lèi)(type())。
?
10. 類(lèi)裝飾器
類(lèi)裝飾器是一種函數(shù),它接受類(lèi)作為輸入并返回類(lèi)作為輸出,例如:
registry = { }
def register(cls):
registry[cls.__clsid__] = cls
return cls
要使用該函數(shù),可以在類(lèi)定義前將它用作裝飾器,例如:
class Foo(object):
__clsid__ = "123-456"
def bar(self):
pass
等同的方式如下:
class Foo(object):
__clsid__ = "123-456"
def bar(self):
pass
register(Foo)
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線(xiàn),公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性?xún)r(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專(zhuān)為企業(yè)上云打造定制,能夠滿(mǎn)足用戶(hù)豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)站題目:[Python]類(lèi)與面向?qū)ο缶幊?創(chuàng)新互聯(lián)
鏈接URL:http://fisionsoft.com.cn/article/dpioed.html