[Python]類與面向?qū)ο缶幊?/h1>

1. class語(yǔ)句

類通常是由函數(shù)、變量和屬性組成的集合。使用class語(yǔ)句可以定義類,例如:

創(chuàng)新互聯(lián)是專業(yè)的德化網(wǎng)站建設(shè)公司,德化接單;提供成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站,網(wǎng)頁(yè)設(shè)計(jì),網(wǎng)站設(shè)計(jì),建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行德化網(wǎng)站開發(fā)網(wǎng)頁(yè)制作和功能擴(kuò)展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團(tuán)隊(duì),希望更多企業(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

在類主體執(zhí)行期間創(chuàng)建的值放在類對(duì)象中,這個(gè)對(duì)象充當(dāng)著命名空間,例如:

Account.num_accunts
Account.__init__
Account.__del__
Account.deposit
Account.withdraw
Account.inquiry

需要注意的是,class語(yǔ)句本身并不創(chuàng)建該類的任何類型。類僅設(shè)置將在以后創(chuàng)建的所有實(shí)例都使用的屬性。類中定義的函數(shù)稱為實(shí)例方法。類的實(shí)例作為第一個(gè)參數(shù)傳遞,根據(jù)約定,這個(gè)參數(shù)稱為self,但所有合法的標(biāo)識(shí)符都可以使用。類變量是可在類的所有實(shí)例之間共享的值。比如上例的num_accounts變量用于跟蹤存在多少個(gè)Account實(shí)例。
?

2. 類實(shí)例

類的實(shí)例是以函數(shù)形式調(diào)用類對(duì)象來(lái)創(chuàng)建的。這種方法將創(chuàng)建一個(gè)新實(shí)例,而后將該實(shí)例傳遞給類的__init__()方法。__init__()方法的參數(shù)包括新創(chuàng)建的實(shí)例self和在調(diào)用類對(duì)象時(shí)提供的參數(shù)。例如:

a = Account("Guido", 1000.00)
b = Account("Bill", 10.00)

通過(guò)將屬性分配給self來(lái)將其保存到實(shí)例中。例如self.name = name表示將name屬性保存在衫例中。使用"."運(yùn)算符可以訪問(wèn)這些屬性以及類屬性,例如:

a.deposit(100.00)
b.withdraw(50.00)
name = a.name

盡管類會(huì)定義命名空間,但它們不會(huì)為在方法體內(nèi)使用的名稱限定范圍。所以在實(shí)現(xiàn)類時(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)建新類的機(jī)制。原始類稱為基類或超類。新類稱為派生類或子類。通過(guò)繼承創(chuàng)建類時(shí),所創(chuàng)建的類將“繼承”其基類定義的屬性。派生類可以重新定義屬性并添加自己的屬性。
在class語(yǔ)句中使用以逗號(hào)分隔的基類名稱列表來(lái)指定繼承。如果沒(mé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ū)嵗念愔姓业狡ヅ漤?xiàng),搜索將會(huì)在基類上進(jìn)行。這個(gè)過(guò)程會(huì)一直繼續(xù)下去,直到?jīng)]有更多的基類可供搜索。子類可以定義自己的__init__()方法。因此,要由派生類調(diào)用基類的__init__()方法來(lái)對(duì)它們進(jìn)行恰當(dāng)?shù)某跏蓟?。如果基類未定義__init__(),就可以忽略這一步。如果不知道基類是否定義了__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í),派生類重新實(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)

但是這種寫法容易引起一些混淆,可以使用另一種方案,用super()函數(shù),例如:

class MoreEvilAccount(EvilAccount):
    def deposit(self, amount):
        self.withdraw(5.00)
        super().deposit(amount)

Python支持多重繼承,通過(guò)讓一個(gè)類列出多個(gè)基類即可指定多重繼承,例如:

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í)際并未使用在自己的類中初始化的fee值。屬性fee是在兩個(gè)不同的基類中定義的類變量,程序使用了其中一個(gè)。要找到使用了多重繼承的屬性,可以在列表中對(duì)所有基類按從“最特殊”的類到“最不特殊”的類。而后在搜索屬性時(shí),就會(huì)按這個(gè)順序搜索列表,直至找到該屬性的第一個(gè)定義。對(duì)于任何給定的類,通過(guò)打印它的__mro__屬性即可查看基類的順序。

?

4. 靜態(tài)方法和類方法

靜態(tài)方法是一種普通函數(shù),就位于類定義的命名空間中。要定義靜態(tài)方法,可使用@staticmethod裝飾器,例如:

class Foo(object):
    @staticmethod
    def add(x, y):
        return x + y

要調(diào)用靜態(tài)方法,只需用類名作為它的前綴,例如:

x = Foo.add(3, 4)

類方法是將類本身作為對(duì)象進(jìn)行操作的方法。類方法使用@classmethod裝飾器定義,根據(jù)約定,類是作為第一個(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. 特性

特性是一種特殊的屬性,訪問(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)單屬性的形式訪問(wèn)后面的方法,無(wú)需添加額外的()來(lái)調(diào)用該方法。方法本身是作為一類特性被隱式處理的,當(dāng)創(chuàng)建一個(gè)實(shí)例然后訪問(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)方法和類方法時(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ì)屬性的訪問(wèn)將由一系列用戶定義的get、set和delete函數(shù)控制。這種屬性控制方式可以通過(guò)描述符對(duì)象進(jìn)一步推廣。描述符就是一個(gè)表示屬性值的對(duì)象,通過(guò)實(shí)現(xiàn)一個(gè)或多個(gè)特殊的__get__()、__set__()和__delete__()方法,可以將描述符與屬性訪問(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è)例子中,類TypedProperty定義了一個(gè)描述符,分配屬性時(shí)它將進(jìn)行類型檢查,例如:

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)情況下,類的所有屬性和方法都是“公共的”。這意味著對(duì)它們的訪問(wèn)沒(méi)有任何限制。在基類中的所有內(nèi)容都會(huì)被 派生類繼承,并可從派生類內(nèi)進(jìn)行訪問(wèn)。這可能導(dǎo)致在派生類中定義的對(duì)象與在基類中定義的對(duì)象之間發(fā)生命名空間沖突,為了解決該問(wèn)題,類中所有以雙下劃線開頭的名稱都會(huì)變成具有_類名__Foo形式的新名稱。例如:

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)開B__spam()
        pass

這種方案似乎隱藏了數(shù)據(jù),但并沒(méi)有嚴(yán)格的機(jī)制來(lái)實(shí)際阻止對(duì)類的“私有”屬性進(jìn)行訪問(wèn)。盡管這種變形似乎是一個(gè)額外的處理步驟,但變形過(guò)程實(shí)際上只在定義類時(shí)發(fā)生一次。而且,名稱變形不會(huì)在getattr()、hasattr()、setattr()或delattr()等函數(shù)中發(fā)生,因?yàn)樵谶@些函數(shù)中,屬性名稱指定為字符串。對(duì)于這些函數(shù),需要顯示使用變形名稱。

?

8. 對(duì)象表示和屬性綁定

在類的內(nèi)部,實(shí)例是使用字典來(lái)實(shí)現(xiàn)的,可以用實(shí)例的__dict__屬性的形式訪問(wèn)該字典。這個(gè)字典包含的數(shù)據(jù)對(duì)每個(gè)實(shí)例而言都是唯一的。對(duì)實(shí)例的修改始終會(huì)反映到局部__dict__屬性中。同樣,如果直接對(duì)__dict__進(jìn)行修改,所做的修改也會(huì)反映在該屬性中。
實(shí)例被特殊屬性__class__鏈接回它們的類。在特殊屬性__base__中將類鏈接到它們的基類。只要使用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)用類的__getattr__()方法(如果已定義)來(lái)查找該屬性。如果這也失敗,就會(huì)拋出AttributeError異常。

?

9. 運(yùn)算符重載

用戶可以定義Python的所有內(nèi)置運(yùn)算符,比如,如果希望向Python添加一種新的數(shù)字類型,可以定義一個(gè)類并在該類中定義__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. 抽象基類

要定義抽象基類,需要使用abc模塊。該模塊定義一個(gè)元類(ABCMeta)和一組裝飾器,可以按如下方式使用:

from abc import ABCMeta, abstractmethod, abstractproperty
class Foo:
    __metaclass__ = ABCMeta
    @abstractmethod
    def spam(self, a, b):
        pass
    @abstractproperty
    def name(self):
        pass

要定義抽象類,需要將其元類如上所示設(shè)置為ABCMeta。因?yàn)槌橄箢惖膶?shí)現(xiàn)離不開元類。在抽象類中,@abstractmethod和@abstractproperty裝飾器指定Foo的子類必須實(shí)現(xiàn)一個(gè)方法或特性。抽象類不能直接實(shí)例化。這一限制也適用于派生類,如果派生類沒(méi)有實(shí)現(xiàn)一個(gè)或多個(gè)抽象方法,那么嘗試創(chuàng)建派生類實(shí)例將會(huì)失敗。
抽象基類支持對(duì)已經(jīng)存在的類進(jìn)行注冊(cè),使其屬于該基類。這是用register()方法完成的,例如:

class Grok(object):
    def spam(self, a, b):
        print("Grok.spam")

Foo.register(Grok)

?

10. 元類

在Python中定義類時(shí),類定義本身將成為一個(gè)對(duì)象。例如:

class Foo(object): pass
isinstance(Foo, object) # True

類對(duì)象的這種創(chuàng)建方式是由一種名為元類的特殊對(duì)象控制的。即元類就是知道如何創(chuàng)建和管理類的對(duì)象。如果查看Foo的類型,將會(huì)發(fā)現(xiàn)它的類型為type。
使用class語(yǔ)句定義新類時(shí),類主體將作為其自己的私有字典內(nèi)的一系列語(yǔ)句來(lái)執(zhí)行。語(yǔ)句的執(zhí)行與正常代碼執(zhí)行過(guò)程相同,只是會(huì)在私有成員上發(fā)生名稱變形。最后,類的名稱、基類列表和字典將傳遞給元類的解構(gòu)函數(shù),以創(chuàng)建相應(yīng)的類對(duì)象。類創(chuàng)建的最后一步,也就是調(diào)用元類type()的步驟,可以自定義。
類可以顯式地指定其元類,這通過(guò)在基類元組中提供metaclass關(guān)鍵字參數(shù)來(lái)實(shí)現(xiàn),例如:

class Foo(metaclass=type)
    __metaclass__ = type
    ...

如果沒(méi)有顯示指定元類, class語(yǔ)句將檢查基類元組中的第一個(gè)條目。在這種情況下,元類與第一個(gè)基類的類型相同。如果沒(méi)有指定基類,class語(yǔ)句將檢查全局變量__metaclass__是否存在。如果找到了該變量,將使用它來(lái)創(chuàng)建類。如果沒(méi)有找到任何__metaclass__值,Python將使用默認(rèn)的元類(type())。
?

10. 類裝飾器

類裝飾器是一種函數(shù),它接受類作為輸入并返回類作為輸出,例如:

registry = { }
def register(cls):
    registry[cls.__clsid__] = cls
    return cls

要使用該函數(shù),可以在類定義前將它用作裝飾器,例如:

class Foo(object):
    __clsid__ = "123-456"
    def bar(self):
        pass

等同的方式如下:

class Foo(object):
    __clsid__ = "123-456"
    def bar(self):
        pass
register(Foo)

當(dāng)前文章:[Python]類與面向?qū)ο缶幊?/a>
地址分享:
http://bm7419.com/article14/goejge.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)建站公司、定制網(wǎng)站網(wǎng)站建設(shè)、做網(wǎng)站、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

成都網(wǎng)站建設(shè)公司