1. 程式人生 > >python中抽象與類

python中抽象與類

前言:

python中所有的資料都是以物件的形式存在,無論是簡單的數字型別還是複雜的程式碼模組。然而python特殊的語法形式巧妙的將實現物件機制的大量細節隱藏了,比如輸入num = 7就可以建立一個值為7的整數物件,並且將這個物件賦值給變數num。只是這個物件包含了加法,乘法之類方法的物件。
抽象可以節省很多工作,實際上它的作用還要更大,它是使得計算機程式可以讓人讀懂的關鍵。計算機非常樂於處理精準和具體的指令,但是人就不一樣,人需要簡潔但不需要具體和精準

當你需要許多相似的行為,但是不同的狀態時,使用物件是最好的選擇

函式


  • 建立函式
    函式是可以呼叫,它執行某種行為並且返回一個值。一般來說,內建的callable函式可以用來判斷函式是否可呼叫。callable( f )
  • 記錄函式
    給函式寫文件,加入註釋或直接在def後面寫上字串
  • 位置引數:定義函式的時候括號裡面的形參,位置很重要,比名字重要
  • 關鍵字引數:使用引數名提供的引數叫做關鍵字引數hello(greeting = "Hello",name = "world"),引數名和值一定要對應,關鍵字引數可以在函式中給引數提供預設值,提供預設值之後,在呼叫的時候就可以提供可以不提供了。
  • 收集位置引數(不限量):有時候讓使用者提供任意數量的引數,在形參之前加*,結果將以元組的形式傳入進函式,==如果與其他的位置引數聯用,則星號自動收集其餘位置的引數。可以以元組的形式傳入形參位置。
  • 收集關鍵字引數(不限量):在函式定義式的括號裡引數前加兩個星號**,然後就可以將不限數量的關鍵字引數以字典的形式傳入函式。可以以字典的形式傳入形參位置。

物件


物件:基本上可以看做資料(特性)以及由一系列可以存取,操作這些資料的方法所組成的集合(即attribute和function),使用物件代替全域性變數和函式的原因有很多,最主要的原因:

  • 多型:可以對不同類的物件使用同樣的操作。任何不知道物件是什麼型別,但是又要對物件“做點什麼”的時候,都會用到多型。可以讓使用者對於不知道是什麼類的物件進行方法呼叫
  • 封裝:對外部世界隱藏物件的工作細節:封裝是可以不用關心物件是如何構建的而直接使用。
  • 繼承:以普通的類為基礎建立專門的類物件:程式設計師不想把同一段程式碼寫好幾次。比如已有類,但是又想建立一個非常類似的類,新的類可能只是新增幾個方法。在編寫新類時,又不想把舊的程式碼全部複製過去。

__init__和self


_init_()是python中一個特殊的函式名,用於根據類的定義建立例項物件。當你宣告__init__( )方法時,第一個引數必須為self。當例項化一個物件時,在記憶體中例項化物件之後,會自動呼叫__init__()方法,將新建的物件作為self傳入。
self引數指向了這個正在被建立的物件本身。

基類(超類)


命名規則:Python程式設計社群有一個普遍認可的約定,函式用小寫字母命名(另外有下劃線進行強調),而類名各個單詞的首字母大寫。

python中的繼承為超類,對個繼承,就是多個超類。

  • 檢視是否為子類:issubclass()
  • 檢視基類:bases( )

覆蓋方法


新建的子類會自動繼承父類的所有資訊,如果子類重新定義一個與父類同名的方法時,子類的方法就會將父類的方法覆蓋掉.例如:下面的程式碼就會將父類的exclaim方法覆蓋掉,init()同理。

class Car():
	def exclaim(self):
		print("im a car")

class YuGo(Car):
	def exclaim(self):
		print("dhnwdhjkewh")

增加屬性,add attribute and method


屬性分為兩類:

  • 類屬性:所有的例項共享的屬性變數
    在累中定義好的變數屬性,有可能在__init()方法中初始化的,有可能沒有初始化的,但是對於所有的新生成的例項,他們的類屬性變數都是相同的。
  • 例項物件屬性:
    如果某個例項在後續的使用中需要新增新的屬性變數,那就可以建立例項屬性變數。在python的機制中,變數是不需要定義就可以直接使用的,所以其屬性也是不需要定義的,可以直接使用。
    在新增屬性的時候,如果一個方法x新增了一個屬性A1,然後,在另外的一個方法y呼叫A1,如果先呼叫了x,再呼叫y,程式不會報錯。但是當沒有呼叫x,而先呼叫y,則會報錯說屬性不存在,只要一個例項,執行了一條語句,像下面的p1.x= 5時,他就相當於在p1中新增了一個屬性(attribute),在後續的過程中就可以在p1中使用這個屬性。
class Point:
pass
p1 = Point()
p2 = Point()
p1.x = 5
p1.y = 4
p2.x = 3
p2.y = 6
print(p1.x, p1.y)
print(p2.x, p2.y)
If we run this code, the two print statements at the end tell us the new attribute
values on the two objects

如上述程式碼,我們給兩個新生成的物件,添加了兩個新的屬性,但是它不會,改變Point()的定義屬性,當新生成一個物件c的時候,c是沒有c.x,c.y的。

super從父類得到幫助

上面說的是覆蓋父類的方法,如果想要呼叫父類的方法怎麼辦?

class Person():
	def __init__(self,name):
		self.name = name

class EmainPerson(Person):
	def __init__(self,name,email):
		super().__init__(name)
		self.email = email

如上,在子類定義__init__()方法時,父類的__init__()方法會被覆蓋。因此,在子類中,父類的初始化方法並不會自動呼叫,我們必須顯示呼叫它

  • 通過super()方法獲取父類Person的定義
  • 子類的__init__()呼叫了Person.init()方法。它會自動將self引數傳遞給父類。因此,你只需要傳遞其他引數就行,上面的例子其他引數是name
  • self.email = email這行新的程式碼真正起到作用將EmailPerson與Person分開

我們可以在定義EmainPerson的時候,重新定義__init__()方法如下:

class EmainPerson(Person):
	def __init__(self,name,email):
		self.name = name
		self.email = email

但是這樣做有悖於使用繼承的初衷,我們應該讓Person完成它自己應該完成的事情,優點:當Person類的定義改變時,使用super可以保證這些改變自動修改,不用手動修改。

self自辯


self必須為類方法的第一個引數。python使用self引數找到正確的物件所包含的特性和方法。
例如:

class Car():
	def exclaim(self):
		print("im a car")

car1 = Car()
car1.exclaim()
car1.exclaim()---->Car.exclaim( car1 )
上述程式碼做了哪些事情?

  • 查詢car物件所屬的類(Car)
  • 把car物件作為self引數傳遞給Car類所包含的exclaim()方法。

例子:

>>> class Point:
... def reset():
... pass
...
>>> p = Point()
>>> p.reset()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: reset() takes no arguments (1 given)

定義屬性(property)的方法:修飾符


attribute譯為特性(下面的name為特性),就是我們常說的物件特性,property譯為屬性
使用屬性對特性進行訪問和設定

class Duck():
	name= none
	def __init(self):
		....
		pass
	def get_name(self):
		....
		pass
	def set_name(self):
		....
		pass	
	name = property(get_name,set_name)

修飾符:定義屬性的方法,
@property,用於指示getter()方法
@name.setter,用於指示setter()方法