成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

假裝用某米賽爾號的角度看Python面向?qū)ο缶幊?

Heier / 3003人閱讀

摘要:而后面函數(shù)返回的是對象中的。中的方法用于初始化類的實例對象。因為某米賽爾號精靈有數(shù)千只使用繼承的方法可以大大減少代碼量且當需要

老玩家回歸:
掛一張目前的陣容


哎, 菜是原罪啊。。。。。。

類和對象

下面我們正式創(chuàng)建自己的類, 這里我們使用Python自定義某米賽爾號的精靈, 代碼如下:

class Elf:
    def setName(self, name):
        self.name = name
    def getName(self):
        return self.name
    def getInfo(self):
        return self       

類的定義就像函數(shù)定義, 用 class 語句替代了 def 語句, 同樣需要執(zhí)行 class 的整段代碼這個類才會生效。進入類定義部分后, 會創(chuàng)建出一個新的局部作用域, 后面定義的類的數(shù)據(jù)屬性方法都是屬于此作用域的局部變量。上面創(chuàng)建的類很簡單, 只有一些簡單的方法。當捕捉精靈后, 首先要為其起名字, 所以我們先編寫函數(shù) setName() 和 getName()。似乎函數(shù)中 self 參數(shù)有點奇怪, 我們嘗試建立具體的對象來探究該參數(shù)的作用。

>>> x = Elf()
>>> y = Elf()
>>> x.setName("小火猴")
>>> y.setName("皮皮")

>>> x.getName()
小火猴
>>> y.getName()
皮皮

>>> x.getInfo()
<__main__.Elf instance at 0xXXXXXXXX>

>>> y.getInfo()
<__main__.Elf instance at 0xXXXXXXXX>

創(chuàng)建對象和調(diào)用一個函數(shù)很相似, 使用類名作為關鍵字創(chuàng)建一個類的對象, 實際上, Elf() 的括號里是可以有參數(shù)的, 后面我們會討論到。我們有兩只精靈, 一只是小火猴, 一只是皮皮, 并且對他們執(zhí)行 getName() , 名字正確返回。觀察 getInfo() 的輸出, 返回的是包含地址的具體對象信息, 可以看到兩個對象的地址, 是不一樣的。Python 中的self作用和 C++ 中的 *this 指針類似, 在調(diào)用 精靈對象 的 setName() 和 getName() 函數(shù)時, 函數(shù)都會自動把該對象的地址作為第一個參數(shù)傳入(該信息包含在參數(shù) self 中), 這就是為什么我們調(diào)用函數(shù)時不需要寫 self , 而在函數(shù)定義時需要把參數(shù)作為第一個參數(shù)。傳入對象地址是相當必要的, 如果不傳入地址, 程序就不知道要訪問類的哪一個對象。

類的每個對象都會有各自的數(shù)據(jù)屬性, Elf 類中有數(shù)據(jù)屬性 name, 這是通過setName() 函數(shù)中的語句 self.name = name創(chuàng)建的。這個語句中的兩個 name 是不一樣的, 它們的作用域不一樣。第一個 name 通過 self 語句聲明的作用域是類 Elf() 的作用域, 將其作為對象 x 的數(shù)據(jù)屬性進行存儲, 而后面的 name 的作用域是函數(shù)的局部作用域, 與參數(shù)中的 name 相同。而后面 getName() 函數(shù)返回的是對象中的 name。

__init__()方法

從更深層邏輯去說, 我們捕捉到精靈的那一刻應該就有名字, 而并非捕捉后去設置。所以這里我們需要的是一個初始化手段。Python中的__init__() 方法用于初始化類的實例對象。__init__() 函數(shù)的作用一定程度上與C++的構造函數(shù)相似, 但并不等于。C++ 的構造函數(shù)是使用該函數(shù)去創(chuàng)建一個類的實例對象, 而Python執(zhí)行__init__() 方法時實例對象已被構造出來。__init__()方法會在對象構造出來后自動執(zhí)行, 所以可以用于初始化我們所需要的數(shù)據(jù)屬性。修改Elf 類的代碼, 代碼如下:

class Elf:
    def __init__(self, name, gender, level):
        self.type = ("fire", None)
        self.gender = gender
        self.name = name
        self.level = level
        self.status = [10+2*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level]
        
    # 精靈體力, 攻擊, 防御, 特攻, 特防, 速度
    def getName(self):
        return self.name
    def getGender(self):
        return self.gender
    def getType(self):
        return self.type
    def getStatus(self):
        return self.status

在此處我們增加了幾個數(shù)據(jù)的屬性: 性別、等級、能力和精靈屬性。連同前面的名字, 都放在__init__()方法進行初始化。數(shù)據(jù)屬性是可以使用任意數(shù)據(jù)類型的, 小火猴屬性是火, 而精靈可能會有倆個屬性, 假設小火猴經(jīng)過兩次進化稱為烈焰猩猩屬性為地面, 火系。為了保持數(shù)據(jù)類型的一致性, 所以我們使用元組存儲, 并讓小火猴的第二個屬性為 None。由于小火猴的屬性是固定的, 所以在__init__() 的輸入?yún)?shù)不需要 type。而精靈的能力會隨著等級的不同而不同, 所以在初始化中也需要實現(xiàn)這一點。我們創(chuàng)建實例對象測試代碼:

>>> x = Elf("小火猴", "male", 5)
>>> y = Elf("皮皮", "female", 6) # 這里有錯誤, 皮皮不是火系, 可以接著往下看, 思考如何改正
>>> print( x.getName(), x.getGender(), x.getStatus() )
小火猴 male [20, 10, 10, 10, 10, 10]

>>> print( y.getName(), y.getGender(), y.getStatus() )
皮皮 female [22, 11, 11, 11, 11, 11]

這時候創(chuàng)建對象就需要參數(shù)了, 實際上這是__init__() 函數(shù)的參數(shù)。__init__() 自動將數(shù)據(jù)屬性進行了初始化, 然后調(diào)用相關函數(shù)能夠返回我們需要的對象的數(shù)據(jù)屬性。

對象的方法

1.方法引用
類的方法和對象的方法是一樣的, 我們在定義類的方時程序沒有為類的方法分配內(nèi)存, 而在創(chuàng)建具體實例對象的程序才會為對象的每個數(shù)據(jù)屬性和方法分配內(nèi)存, 我們已經(jīng)知道定義類的方法是 def 定義的, 具體定義格式與普通函數(shù)相似, 只不過類的方法的第一個參數(shù)要為 self 參數(shù)。我們可以用普通函數(shù)實現(xiàn)對對象函數(shù)的引用:

>>> x = Elf("小火猴", "male", 5)
>>> getStatus1 = x.getStatus
>>> getStatus1()
[20, 10, 10, 10, 10, 10]

雖然看上去似乎是調(diào)用了一個普通函數(shù), 但是 getStatus1() 這個函數(shù)是引用 x.getStatus() 的, 意味著程序還是隱性地加入了 self 參數(shù)。

2.私有化
先敲代碼:

>>> x.type
("fire", None)
>>> x.getType()
("fire", None)

雖然這樣似乎很方便, 但違反了類的封裝原則。對象的狀態(tài)對于類外部應該是不可以訪問的。為何要這樣做, 我們查看Python 的模塊源碼時會發(fā)現(xiàn)源碼里定義了很多類, 模塊中算法通過使用類是很常見的, 如果我們使用算法時能隨意訪問對象中的數(shù)據(jù)屬性, 那么很有可能在不經(jīng)意間修改算法中已經(jīng)調(diào)好的參數(shù), 這是十分尷尬的。盡管我們不會可以那么去做, 但這種無意的改動是常有的事。一般封裝好的類都會有足夠的函數(shù)接口供程序員用, 程序員沒有必要訪問對象的具體數(shù)據(jù)類型。

為防止程序員無意間修改了對象的狀態(tài), 我們需要對類的數(shù)據(jù)屬性和方法進行私有化。Python 不支持直接私有方式, 但可以使用一些小技巧達到私有特性的目的。為了讓方法的數(shù)據(jù)屬性或方法變?yōu)樗接? 只需要在它的名字前面加上雙下劃線即可, 修改Elf 類代碼:

# 自定義類
class Elf:
    def __init__(self, name, gender, level):
        self.__type = ("fire", None)
        self.__gender = gender
        self.__name = name
        self.__level = level
        self.__status = [10+2*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level]
        
    # 精靈體力, 攻擊, 防御, 特攻, 特防, 速度
    def getName(self):
        return self.__name
    def getGender(self):
        return self.__gender
    def getType(self):
        return self.__type
    def getStatus(self):
        return self.__status
    def level_up(self):
        self.__status = [s+1 for s in self.__status]
        self.__status[0] += 1 # HP每級增加2點, 其余增加1點
    def __test(self):
        pass
>>> x = Elf("小火猴", "male", 5)
>>> print(x.type)
Traceback (most recent call last):
  File "seer.py", line 25, in 
    print(x.type)
AttributeError: "Elf" object has no attribute "type"
>>> print(x.getName())
小火猴
>>> x.test()
Traceback (most recent call last):
  File "ser.py", line 28, in 
    x.test()
AttributeError: "Elf" object has no attribute "test"

現(xiàn)在在程序外部直接訪問私有數(shù)據(jù)是不允許的, 我們只能通過設定好的節(jié)后函數(shù)去調(diào)取對象信息。不過通過雙下劃綫實現(xiàn)的私有實際上是"偽私有化", 實際上我們還是可以做到從外部訪問這些私有屬性。

>>> print(x._Elf__type)
("fire", None)

Python 使用的是一種 name_mangling 技術, 將__membername 替換成 _class__membername, 在外部使用原來的私有成員時, 會提示無法找到, 而上面執(zhí)行的 x.Elf__type 是可以訪問。簡而言之, 確保其他人無法訪問對象的方法和數(shù)據(jù)屬性是不可能的, 但是使用這種 name_mangling 技術是一種程序員不應該從外部訪問這些私有成員的強有力信號。

可以看到代碼中還增加了一個函數(shù)level_up(), 這個函數(shù)用于處理精靈升級是能力的提升, 我們不應該在外部修改 x 對象的 status , 所以應準備好接口去處理能力發(fā)生變化的情景, 函數(shù) level_up() 僅僅是一個簡單的例子, 而據(jù)說在工業(yè)代碼中, 這樣的函數(shù)接口是大量的, 程序需要對它們進行歸類并附上相應的文檔說明。

3.迭代器
Python容器對象(列表、元組、字典和字符串等)都可以可以用 for 遍歷,

for element in [1, 2, 3]:
    print(element)

這種風格十分簡潔, for 語句在容器對象上調(diào)用了 iter(), 該函數(shù)返回一個定義了 next() 方法的迭代器對象, 它在容器中逐一訪問元素。當容器遍歷完畢, __next__() 找不到后續(xù)元素時, next() 找不到后續(xù)元素時, next()會引發(fā)一個 StopIteration 異常, 告知for循環(huán)終止。

>>> L = [1, 2, 3]
>>> it = iter(L)
>>> it

>>> it.__next__()
1
>>> it.__next__()
2
>>> it.__next__()
2
>>> it.__next__()
3
>>> it.__next__()
Traceback (most recent call last):
  File "", line 1, in 
StopIteration

當知道迭代器協(xié)議背后的機制后, 我們便可以吧迭代器加入到自己的類中。我們需要定義一個__iter__()方法, 它返回一個有 next() 方法的對象, 如果類定義了next(), __iter__()可以只返回self, 再次修改類 Elf 的代碼, 通過迭代器能輸出對象的全部信息。

class Elf:
    def __init__(self, name, gender, level):
        self.__type = ("fire", None)
        self.__gender = gender
        self.__name = name
        self.__level = level
        self.__status = [10+2*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level]
        self.__info = [self.__name, self.__type, self.__gender, self.__level, self.__status]
        self.__index = -1
        
    # 精靈體力, 攻擊, 防御, 特攻, 特防, 速度
    def getName(self):
        return self.__name
    
    def getGender(self):
        return self.__gender
    
    def getType(self):
        return self.__type

    def getStatus(self):
        return self.__status
        
    def level_up(self):
        self.__status = [s+1 for s in self.__status]
        self.__status[0] += 1
    
    def __iter__(self):
        print("名字 屬性 性別 等級 能力")
        return self
    
    def next(self):
        if self.__index == len(self.__info) - 1:
            raise StopIteration
        self.__index += 1
        return self.__info[self.__index]
繼承

面向?qū)ο缶幊痰暮锰幹痪褪谴a的復用, 實現(xiàn)這種重用的方法之一就是通過繼承機制。繼承是兩個類或多個類之間的父子關系, 子類繼承了基類的所有公有數(shù)據(jù)屬性和方法, 并且可以通過編寫子類的代碼擴充子類的功能??梢哉f, 如果人類可以做到兒女繼承了父母的所有才學并加以拓展, 那么人類的發(fā)展至少是現(xiàn)在的數(shù)萬倍。繼承實現(xiàn)了數(shù)據(jù)屬性和方法的重用, 減少了代碼的冗余度。

那么我們?nèi)绾螌崿F(xiàn)繼承呢??如果我們需要的類中具有公共的成員, 且具有一定的遞進關系, 那么就可以使用繼承, 且讓結構最簡單的類作為基類。一般來說, 子類是父類的特殊化, 如下關系:

                哺乳類動物 ————> 貓科動物 ————> 東北虎

東北虎類 繼承 貓科動物類, 貓科動物類 繼承 哺乳動物類, 貓科動物類編寫了所有貓科動物公有的行為的方法而特定貓類則增加了該貓科動物特有的行為。不過繼承也有一定弊端, 可能基類對于子類也有一定特殊的地方, 如果某種特定貓科動物不具有絕大多數(shù)貓科動物的行為, 當程序員嗎,沒有理清類之間的關系是, 可能使子類具有不該有的方法。另外, 如果繼承鏈太長的話, 任何一點小的變化都會引起一連串變化, 我們使用的繼承要注意控制繼承鏈的規(guī)模。

繼承語法: class 子類名(基類名1, 基類名2,...), 基類卸載括號里, 如果有多個基類, 則全部寫在括號里, 這種情況稱為多繼承。在Python中繼承有以下一些特點:

1) 在繼承中積累初始化方法__init__()函數(shù)不會被自動調(diào)用。如果希望子類調(diào)用基類的__init__() 方法, 需要在子類的 __init__() 方法中顯示調(diào)用它。這與C++差別很大。

2) 在調(diào)用基類的方法時, 需要加上基類的類名前綴, 且?guī)?self 參數(shù)變量, 注意在類中調(diào)用該類中定義的方法時不需要self參數(shù)。

3) Python總是首先查找對應類的方法, 如果在子類中沒有對應的方法, Python才會在繼承鏈的基類中按順序查找。

4) 在Python繼承中, 子類不能訪問基類的私有成員。

這是最后一次修改類Elf的代碼:

class pokemon:
    def __init__(self, name, gender, level, type, status):
        self.__type = type
        self.__gender = gender
        self.__name = name
        self.__level = level
        self.__status = status
        self.__info = [self.__name, self.__type, self.__gender, self.__level, self.__status]
        self.__index = -1
        
    # 精靈體力, 攻擊, 防御, 特攻, 特防, 速度
    def getName(self):
        return self.__name
    
    def getGender(self):
        return self.__gender
    
    def getType(self):
        return self.__type

    def getStatus(self):
        return self.__status
        
    def level_up(self):
        self.__status = [s+1 for s in self.__status]
        self.__status[0] += 1
    
    def __iter__(self):
        print("名字 屬性 性別 等級 能力")
        return self
    
    def next(self):
        if self.__index == len(self.__info) - 1:
            raise StopIteration
        self.__index += 1
        return self.__info[self.__index]
        
        
class Elf(pokemon):
    def __init__(self, name, gender, level):
        self.__type = ("fire", None)
        self.__gender = gender
        self.__name = name
        self.__level = level
        self.__status = [10+2*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level, 5+1*level]
        pokemon.__init__(self, self.__name, self.__gender, self.__level, self.__type, self.__status)
>>> x = Elf("小火猴", "male", 5)
>>> print(x.getGender())
male
>>> for info in x:
        print(info)
小火猴 ("fire", None) male 5 [20, 10, 10, 10, 10, 10]        

我們定義了Elf 類的基類pokemon, 將精靈共有的行為都放到基類中, 子類僅僅需要向基類傳輸數(shù)據(jù)屬性即可。這樣就可以很輕松地定義其他基于pokemon類的子類。因為某米賽爾號精靈有數(shù)千只, 使用繼承的方法可以大大減少代碼量, 且當需要對全部精靈進行整體改變時僅需改變pokemanl類的__init__()即可, 并向基類傳輸數(shù)據(jù), 這里注意要加self參數(shù), Elf 類沒有繼承基類的私有數(shù)據(jù)屬性, 因此在子類只有一個self.__type, 不會出現(xiàn)因繼承所造成的重名情況。為了能更加清晰地描述這個問題, 這里再舉一個例子:

class animal:
    def __init__(self):
        self.__age = age
    def print2(self):
        pritn(self.__age)

class dog(animal):
    def__init__(self, age):
        animal.__init__(self, age)
    def print2(self):
        print(self.__age)
>>> a_animal = animal(10)
>>> a_animal.print2()
10
>>> a_dog = dog(10)
>>> a_dog.print2()
Traceback (most recent call last):
  File "seer.py", line 13, in 
    a_dog.print2()
  File "seer.py", line 11, in print2
    print(self.__age)
AttributeError: "dog" object has no attribute "_dog__age"
That"s all ! (時間有限, 我會不斷完善修改的)

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://systransis.cn/yun/43050.html

相關文章

  • Python Pandas讀取修改excel操作攻略

    環(huán)境:python 3.6.8 以某米賽爾號舉個例子吧: showImg(https://segmentfault.com/img/bVboqzz?w=396&h=215);showImg(https://segmentfault.com/img/bVboqzA?w=362&h=216); >>> pd.read_excel(1.xlsx, sheet_name=Sheet2) 名字 ...

    frolc 評論0 收藏0
  • Docker正方登場——未來正在遠方……

    摘要:并不是因為它是閃亮的新事物或者它是一些虛構的最佳實踐,而是因為像亞馬遜或者已經(jīng)在這上面投入了年的心血,他們告訴了我們?nèi)绾螛嫿ㄕ嬲幸?guī)模的系統(tǒng)。截止目前,我們已經(jīng)部署了由亞馬遜等提供的重量級虛擬化服務器。 周一時候數(shù)人云與大家分享了一篇關于Docker的反方言論——《一份Docker的反方辯論——我還是用Heroku好了》,一周之后,同樣的作者,又為Docker正名,寫了一篇正方言論。D...

    waruqi 評論0 收藏0
  • JavaScript設計模式與開發(fā)實踐 | 01 - 面向對象的JavaScript

    摘要:在中,并沒有對抽象類和接口的支持。例如,當對象需要對象的能力時,可以有選擇地把對象的構造器的原型指向?qū)ο螅瑥亩_到繼承的效果。本節(jié)內(nèi)容為設計模式與開發(fā)實踐第一章筆記。 動態(tài)類型語言 編程語言按數(shù)據(jù)類型大體可以分為兩類:靜態(tài)類型語言與動態(tài)類型語言。 靜態(tài)類型語言在編譯時已確定變量類型,動態(tài)類型語言的變量類型要到程序運行時,待變量被賦值后,才具有某種類型。 而JavaScript是一門典型...

    suxier 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<