python自动化开发是什么_Python自动化开发学习6
引子
假設我們要在我們的程序里表示狗,狗有如下屬性:名字、品種、顏色。那么可以先定義一個模板,然后調用這個模板生成各種狗。
def dog(name,d_type,color):
data = {
'name':name,
'd_type':d_type,
'color':color}
return data
d1 = dog('小七','拉布拉多','黃')
d2 = dog('旺財','中華田野犬','黃')
上面用函數作為模板來定義數據確實很方便。然后我們要讓狗叫,那么還需要在寫一個程序,簡單點,比如是這樣的
def dog(name,d_type,color):
data = {
'name':name,
'd_type':d_type,
'color':color}
return data
d1 = dog('小七','拉布拉多','黃')
d2 = dog('旺財','中華田野犬','黃')
def bark(d):
print("dog %s:Wang~Wang~Wang~~~"%d['name'])
bark(d2)
貌似也很完美,但是如果這是bark不小心引用了別的參數,比如一只貓,那么問題就來了。
def dog(name,d_type,color):
data = {
'name':name,
'd_type':d_type,
'color':color}
return data
d1 = dog('小七','拉布拉多','黃')
d2 = dog('旺財','中華田野犬','黃')
def cat(name,c_type,color):
data = {
'name':name,
'c_type':c_type,
'color':color}
return data
c1 = cat('比格沃斯','波斯貓','白') # 在定義一個貓
def bark(d):
print("dog %s:Wang~Wang~Wang~~~"%d['name'])
def miaow(c):
print("cat %s:Miao~Miao~Miaow~~~"%c['name'])
bark(c1) # 這里是不是亂套了
miaow(d1)
所以這里的問題就是,我們希望bark只能調用狗,cat只能調用貓。當然可以加上if判斷,但是很low。于是可以改成這樣:
def dog(name,d_type,color):
def bark():
print("dog %s:Wang~Wang~Wang~~~"%name)
data = {
'name':name,
'd_type':d_type,
'color':color,
'action':bark} # 把動作的函數寫到狗的函數里,把函數名作為返回字典里的一個元素
return data
d1 = dog('小七','拉布拉多','黃')
d2 = dog('旺財','中華田野犬','黃')
def cat(name,c_type,color):
def miaow():
print("cat %s:Miao~Miao~Miaow~~~"%name)
data = {
'name':name,
'c_type':c_type,
'color':color,
'action':miaow}
return data
c1 = cat('比格沃斯','波斯貓','白')
d1['action']() # 這樣調用狗模板里的狗叫函數
c1['action']()
把狗叫的函數也寫到狗的模板里去,然后就實現了只能由狗調用狗叫的需求了。
引子講完了,上面并不是面向對象。上面的問題也不是很復雜,但是如果問題再復雜可能就解決不下去了,我們需要面向對象來解決這類問題。
面向過程 VS 面向對象
編程范式
編程是程序員用特定的語法+數據結構+算法組成的代碼來告訴計算機如何執行任務的過程,實現一個任務的方式有很多種不同的方式,對這些不同的編程方式的特點進行歸納總結得出來的編程方式類別,即為編程范式。兩種最重要的編程范式分別是面向過程編程和面向對象編程,然后還有一個函數式編程。
面向過程編程(Procedural Programming)
面向過程編程,就是程序從上到下一步步執行,一步步從上到下,從頭到尾的解決問題。基本設計思路就是程序一開始是要著手解決一個大的問題,然后把一個大問題分解成很多個小問題或子過程,這些子過程再執行的過程再繼續分解直到小問題足夠簡單到可以在一個小步驟范圍內解決。
這樣做的問題也是顯而易見的,就是如果你要對程序進行修改,對你修改的那部分有依賴的各個部分你都也要跟著修改,隨著程序越來越大,這種編程方式的維護難度會越來越高。
但是面向過程依然是有可取之處的,如果你只是寫一些簡單的腳本,去做一些一次性任務,用面向過程的方式是極好的,但如果你要處理的任務是復雜的,且需要不斷迭代和維護的,那還是用面向對象最方便了。
面向對象編程(Object-Oriented Programming)
OOP編程是利用“類”和“對象”來創建各種模型來實現對真實世界的描述,使用面向對象編程的原因一方面是因為它可以使程序的維護和擴展變得更簡單,并且可以大大提高程序開發效率。另外,基于面向對象的程序可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。
面向對象編程介紹
無論用什么形式來編程,我們都要明確記住以下原則:
寫重復代碼是非常不好的低級行為
你寫的代碼需要經常變更
面向對象的核心特性
Class 類
Object 對象
Encapsulation 封裝
Inheritance 繼承
Polymorphism 多態
Class 類 和 Object 對象
類:一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法。
對象:一個對象即是一個類的實例化后實例,一個類必須經過實例化后方可在程序中調用,一個類可以實例化多個對象,每個對象亦可以有不同的屬性。
首先要定義類,然后將類實例化,最后通過這個實例來調用類中的功能
這里類名要用大駝峰規范來命名
# class是定義類,Dog是類名,括號中的object暫時知道是必填就好了
class Dog(object):
print("Hello , I am a dog.")
# 上面已經定義好了類,下面來將類實例化
d = Dog() # 但是這里會把print打印出來
我們不希望在實例化的時候就把print打印出來,而是要在調用類中的功能的時候再打印,那么上面的類還得修改一下。
class Dog(object):
# 定義一個函數,把print寫到函數里,避免被直接調用執行
def sayhi(self):
print("Hello , I am a dog.")
d = Dog() # 這是這里就不會直接打印了
d.sayhi() # 這樣來調用執行類里的sayhi
d = Dog() 這步叫實例化。先去實例化,然后 d.sayhi() 再去調用它的功能
上面的例子我們沒有傳入參數,現在把引子里的例子改成類,傳入屬性:
class Dog(object):
# 參數要寫在__init__這個函數里,這個叫構造函數或構造方法
def __init__(self,name,d_type,color):
self.name = name # 將傳入的傳輸傳給self
self.type = d_type
self.color = color
# 下面的函數叫做類的方法
def bark(self):
print("dog %s: Wang~Wang~Wang~~~"%self.name) # 這里就可以調用self里的值了
d1 = Dog('旺財','中華田野犬','黃') # 先實例化
d1.bark() # 然后調用功能
self,就是實例本身。你實例化時python會自動把這個實例本身通過self參數傳進去。上面例子中的self就是d1。再通俗一點,self就是調用當前方法的對象。
實例化后產生的對象,就叫實例,是這個類的實例。
d1 = Dog('旺財','中華田野犬','黃') 這個就是進行實例化,產生了Dog這個類的一個實例d1,而self就是這個實例本身
上面的__init__叫做構造函數,或者叫構造方法,也就是初始化的方法
上面的bark函數叫做類的方法,我們可以根據需要寫多個方法
我們寫一個給狗吃東西的函數food,把食物通過參數傳入
再寫一個給狗改名字的函數,這里牽涉到修改對象的屬性值,也就是初始化的內容可以后期修改
再寫一個自報名字的函數,看看改名的效果:
class Dog(object):
def __init__(self,name,d_type,color):
self.name = name
self.type = d_type
self.color = color
def bark(self):
print("dog %s: Wang~Wang~Wang~~~"%self.name)
def eat(self,food):
print("%s 正在吃 %s"%(self.name,food))
def rename(self,new_name):
"給狗改名"
print("%s 改名為 %s"%(self.name,new_name))
self.name = new_name # 這里改變了對象的屬性
def say_name(self):
"報名字"
print("我的名字是:%s"%self.name)
d1 = Dog('旺財','中華田野犬','黃')
d1.bark()
d1.eat("骨頭") # 把骨頭傳給了food
d1.say_name() # 現在的名字
d1.rename("小黃") # 改名
d1.say_name() # 再看看名字變了沒
總結:
類 ==》 實例化 ==》 實例(對象):類經過實例化后變成了實例也就是對象
__init__:構造函數
self.name = name :屬性,或者叫成員變量、字段
def bark(self) :方法,或者叫動態屬性
Encapsulation 封裝
封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏
私有屬性
之前使用的屬性并不是公有屬性,而是叫成員屬性,這些屬性是只屬于它的對象的。公有屬性最后講。
私有屬性在類的外部不不可訪問的。我們來增加一個私有屬性hp,然后寫一個函數來操作hp這個私有屬性。定義私有屬性使用self.__屬性名
class Cat(object):
def __init__(self,name,c_type,color,hp=100):
self.name = name
self.c_type = c_type
self.color = color
self.__hp = hp # 定義為私有屬性
def be_hit(self,damage):
"造成傷害,扣除hp值"
print("%s 受到了 %d點 傷害"%(self.name,damage))
print("當前hp:%d,傷害:%d,剩余hp:%d"
%(self.__hp,damage,self.__hp-damage))
self.__hp -= damage
c1 = Cat('比格沃斯','波斯貓','白')
print("name:",c1.name) # 公有屬性可以正常獲取到
#print(c1.__hp) # 這句會報錯,類的外部是獲取不到私有屬性的
c1.be_hit(10) # 這里是通過類內部的方法打印的私有屬性的數值
c1.be_hit(5)
既然可以通過內部方法訪問私有屬性,我們可以將私有屬性寫到函數里return,提供一個給外部訪問的方法,但是不能修改。另外其實也是有方法可以強制訪問的。
class Cat(object):
def __init__(self,name,c_type,color,hp=100):
self.name = name
self.c_type = c_type
self.color = color
self.__hp = hp
def be_hit(self,damage):
print("%s 受到了 %d點 傷害"%(self.name,damage))
print("當前hp:%d,傷害:%d,剩余hp:%d"
%(self.__hp,damage,self.__hp-damage))
self.__hp -= damage
def get_hp(self):
"提供方法,返回私有屬性hp的值"
return self.__hp
c1 = Cat('比格沃斯','波斯貓','白')
print(c1.get_hp()) # 通過內部提供的方法來訪問私有屬性
print(c1._Cat__hp) # 其實也可以強制訪問到私有屬性,并這個是可以修改私有屬性值的
對象名._類名__私有屬性名 : 強制訪問私有屬性,可讀寫
公有屬性
公有屬性,所有屬于這個類的對象都可以訪問的屬性,才叫公有屬性。
之前的例子中,類Dog定義了2個對象d1 和 d2 ,但是d1里的屬性是只屬于d1的,無法通過d2來訪問,這些都是叫成員屬性
在類里直接定義的屬性,既公有屬性。
class Dog(object):
called = "狗" # 在類里定義公有屬性
def __init__(self,name,d_type,color):
self.name = name
self.type = d_type
self.color = color
d1 = Dog('旺財','中華田野犬','黃')
d2 = Dog('小七','拉布拉多','黃')
print(d1.called)
print(d2.called)
Dog.called = "犬" # 通過類更改類公有屬性
print(d1.called) # 通過類修改,所有對象都會變
print(d2.called)
d1.called = "看門狗" # 通過對象更改,其實這句是新建了d1里的成員屬性
# 同時有成員屬性和公有屬性,則使用成員屬性
print(d1.called) # 這里打印的是剛才新建的成員屬性
print(d2.called) # 這里還是原來的公有屬性
Dog.called = "dog" # 再通過類更改公有屬性
print(d1.called) # 這里沒有變化,因為成員屬性還在,并且沒變
print(d2.called) # 這里變化了,因為這里只有公有屬性
del d1.called # 把成員屬性從內存中清楚
print(d1.called) # 現在全部是公有屬性值了
print(d2.called)
d1.called = "看門狗"
d2.called = "導盲犬" # 現在全部都有成員屬性了
print(d1.called)
print(d2.called)
print(Dog.called) # 直接通過類而不是對象獲取的一定是公有屬性
這里公有屬性和成員屬性的情況和之前學的全局變量和局部變量是一樣的效果。
展開講一下函數的情況,類中def定義的所有函數我們也可以理解為是公有的。那么也可以通過定義成員的方法來替換原來的公有的方法:
class Dog(object):
called = "狗"
def __init__(self,name,d_type,color):
self.name = name
self.type = d_type
self.color = color
def bark(self):
print("dog %s: Wang~Wang~Wang~~~"
%self.name)
'''
在類外面再定義一個函數,這里無法使用self傳入變量了
如果要傳入參數,那么下面調用的時候也得帶參數傳入,這樣調用方法也變了
'''
def bark():
"自己再定義一個bark"
print("TEST")
d1 = Dog('旺財','中華田野犬','黃')
d2 = Dog('小七','拉布拉多','黃')
d1.bark() # 正常調用類中的方法
d2.bark()
d1.bark = bark # 通過這個方法來實現給d1一個自己的成員方法
d1.bark() # 現在調用的不是公有方法,而是d1自己的成員方法
d2.bark()
del d1.bark # 清除d1的bark方法后,d1有可以正常調用類的bark方法了
d1.bark()
d2.bark()
析構方法 和 構造方法
方法和函數:在類里面定義的函數就是這個類的方法,所以方法和函數這兩個詞有時候會混用,主要看你是在描述什么東西
構造方法:之前在為類傳入參數的時候用到了構造函數,構造函數其實就是在生成1個實例的時候自動運行的函數,所以通過構造函數我們可以實現在生成實例的時候自動把參數傳遞給self
析構方法:和構造方法差不多,就是在一個實例被銷毀的時候自動運行的函數
class test(object):
def __init__(self): # 構造函數
print("init in the test")
def __del__(self): # 析構函數
print("del in the test")
input("準備實例化對象")
obj = test()
input("準備銷毀對象")
del obj
input("執行完畢")
把一些收尾的工作寫在析構函數里,在你銷毀這個對象的時候就自動執行,比如關閉所有的客戶連接、關閉所有打開的文件等等。具體怎么用得到了以后用的時候才知道了。
Inheritance 繼承
繼承是指這樣一種能力:它可以使用現有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴展。
通過繼承創建的新類稱為“子類”或“派生類”。
被繼承的類稱為“基類”、“父類”或“超類”。
繼承的過程,就是從一般到特殊的過程。
要實現繼承,可以通過“繼承”(Inheritance)和“組合”(Composition)來實現。
要同時繼承多個類有2種方法:多重繼承和多級繼承
繼承概念的實現方式主要有2種:實現繼承、接口繼承。
實現繼承是指使用基類的屬性和方法而無需額外編碼的能力
接口繼承是指僅使用屬性和方法的名稱,但是子類必須提供實現的能力(子類重構父類方法)
抽象類:僅定義將由子類創建的一般屬性和方法。
OOP開發范式大致為:劃分對象→抽象類→將類組織成為層次化結構(繼承和合成) →用類與實例進行設計和實現幾個階段。
簡單的繼承例子:
class Human(object):
def talk(self):
print("Hello World")
class Chinese(Human): # 這樣就繼承了Human這個類
pass # 什么也不寫,但是我們有繼承啊
h1 = Chinese() # 實例化1個chinese的對象
h1.talk() # 雖然Chinaes沒有talk方法,但是繼承了Human的talk方法
子類也可以有自己的方法,還可能重構父類的方法:
class Human(object):
def talk(self):
print("Hello World")
class Chinese(Human): # 這樣就基礎了Human這個類
"這次我們來寫幾個方法"
def greatWall(self): # 定義一個新的方法
print("長城長啊長")
def talk(self): # 重構父類的方法
print("你好,世界")
h1 = Chinese() # 實例化1個chinese的對象
h1.talk() # 調用的是重構后的新方法
h1.greatWall() # 在子類中定義的新方法
再加上類的參數:
先只寫上父類的構造函數,子類不寫。子類就是完全繼承父類構造函數。
class Human(object):
def __init__(self,name,age): # 父類里有構造函數
self.name = name
self.age = age
def talk(self):
print("Hello World")
class Chinese(Human):
pass # 子類沒有構造函數,就直接繼承父類的構造函數
#h1 = Chinese() # 沒有參數會報錯,因為構造函數需要傳入2個參數
h1 = Chinese("張三",33) # 實例化的時候,需要根據構造函數的要求傳入參數
h1.talk()
再來看子類的屬性,上面是完全繼承父類的屬性,那么就不用寫構造函數。
也可以完全無視父類的屬性,那么直接重構自己的構造函數就好了。
復雜一點情況,繼承父類的屬性,但是還要有自己的額外屬性。
class Human(object):
def __init__(self,name,age): # 父類的構造函數
self.name = name
self.age = age
def talk(self):
print("Hello World")
class Chinese(Human):
def __init__(self,name,age,kungfu): # 先繼承,再重構
#Human.__init__(self,name,age) # 調用父類的構造函數,實現繼承
super(Chinese,self).__init__(name,age) # 和上面那句效果一樣,用這種寫法
self.kungfu = kungfu # 再增加自己重構的屬性
def talk(self): # 這里順便把函數也重構了,但是我們要保留父類里的部分
Human.talk(self) # 方法也可以和屬性一樣,實現繼承和重構
print("你好,世界")
h1 = Chinese("張三",33,"少林") # 實例化的時候,需要根據構造函數的要求傳入參數
h1.talk()
print(h1.__dict__) # 順便來看一下這個的作用
子類要繼承父類屬性并且有自己的特有屬性,需要先繼承,再重構。通過父類的名字調用執行父類的構造函數,實現繼承,然后可以在后面寫上自己需要重構的代碼。
函數也是可以用這個方法來調用父類的方法,添加自己的代碼,實現在父類的基礎上重構自己特有的部分
__dict__ 屬性:可以查看對象所有的屬性和值,以字典的形式。
多繼承
class Chinese(Human): 這個是繼承的語法,括號中的變量可以傳入多個類,用逗號隔開就實現了多繼承。比如:
class Chinese(Human,Person): 上課說用的不多,也沒展開。所以暫時就知道這個語法就好了。
下面也是在多繼承的時候才會有區別的內容
新式類 和 經典類
python3里已經沒有這個問題了,并且現在都是用的新式類的寫法。不過還是舉個例子說明一下繼承的順序,這個是多繼承的情況下會遇到的問題。
定義一個基類A,然后是A的兩個子類B和C。最后來個孫子類D,D要繼承B和C。每個類里都定義一個屬性n,寫在構造方法里
class A(object):
pass
def __init__(self):
self.n = "A"
class B(A):
pass
def __init__(self):
self.n = "B"
class C(A):
pass
def __init__(self):
self.n = "C"
class D(B,C):
pass
#def __init__(self):
#self.n = "D"
d1 = D()
print(d1.n)
如果D有構造方法,那么結果一定是D。然后依次將上面的構造方法也注釋掉,看看D的繼承順序。結果是B-C-A。這個叫廣度查找。
經典類就不試了,要在python2里才會有深度查找的效果。在python3里還是廣度查找。
新式類,廣度查找 B-C-A
經典類,深度查找 B-A-C
語法上的區別,都用新式類就好了,經典類知道一下,看到的時候別不認識。
定義類的語法:
class A: # 經典類寫法,python2里和下面的寫法有區別。python3里不必顯示聲明,這么寫也是新式類了。
class A(object): # 新式類寫法,咱就這么寫
調用父類方法的語法:
Human.__init__(self,name,age) # 經典類寫法,這個是子類構造函數里實現先繼承的那句代碼
super(Chinese,self).__init__(name,age) # 新式類寫法,咱就這么寫
不單是構造函數,其他函數也一樣,盡量都super,不是多繼承的話兩個都一樣。但是絕對不要混用。
子類完全繼承父類的通用形式:
super(Son, self).__init__(*args, **kwargs) # 這里Son代指這個子類的類名。
這樣不需要知道父類里有多少參數,總之是把所有父類的參數都繼承過來了。一般這句寫在子類的構造函數的開頭,然后后面再寫上子類需要在構造函數里附加的內容。
Polymorphism 多態
多態,簡單點說:"一個接口,多種實現",指一個基類中派生出了不同的子類,且每個子類在繼承了同樣的方法名的同時又對父類的方法做了不同的實現,這就是同一種事物表現出的多種形態。
多態允許將子類的對象當作父類的對象使用,某父類型的引用指向其子類型的對象,調用的方法是該子類型的方法。這里引用和調用方法的代碼編譯前就已經決定了,而引用所指向的對象可以在運行期間動態綁定
封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是為了,代碼重用。而多態則是為了實現另一個目的,接口重用。多態的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。
舉例:Pyhon 很多語法都是支持多態的,比如 len(),sorted(), 你給len傳字符串就返回字符串的長度,傳列表就返回列表長度。
講了那么多,到底是要通過多態做什么?就是要,通過父類調子類,python不直接支持,但是可以間接支持。
class People(object): # 先定義一個基類
def talk(self): # 基類的talk方法,我們不希望被調用,寫一個會拋出錯誤的代碼
"如果基類的這個方法被調用,就拋出一個錯誤"
raise NotImplementedError("Subclass must implement abstract method")
class Chinese(People): # 這個是子類
def talk(self): # 重構talk方法
print("你好,世界")
class American(People):
def talk(self):
print("Hello World")
# 如果調用了基類的方法,會根據raise里定義的,拋出一個錯誤。去掉下面的注釋測試一下
#p1 = People() # 實例化一個基類
#p1.talk() # 調用基類的talk方法
# 實例化2個對象
c1 = Chinese()
a1 = American()
# 通過子類調用自己的方法當然沒問題。要用多態就是要使用統一的接口來實現這2條命令
c1.talk()
a1.talk()
# 多態是要用父類調用子類
#People.talk(c1) # 這樣是最好的,真正的直接實現多態的方法,但是Python不支持
#People.talk(a1)
# 間接支持多態的方法,定義一個函數作為統一的接口
def People_talk(obj):
obj.talk()
# 用新定義的接口,調用不同的子類,每次用的都是這個子類里重構的那個方法
People_talk(c1) # 傳入一個c1對象,實際就是執行c1.talk()
People_talk(a1) # 傳入一個a1對象,實際就是執行a1.talk()
間接支持多態,新定義一個函數,用參數傳入一個對象。然后再函數中調用這個對象的一個方法。
主動拋出一個錯誤,上面在基類里使用了一個raise命令,可以實現主動觸發一個錯誤,可以定義這個錯誤的類型和消息。這里的作用就是驗證這個方法沒有被調用到,貌似一般都是用一句print來驗證的,這個也很高級。
補充
面向對象的應用場景
學了那么多面向對象,但是什么時候用呢?畢竟在python里我們使用面向過程的方法也是一樣可以實現的。課上總結了3種場景,推薦使用面向對象的方法來實現,并且確實更好。
根據一個模板來創建某些東西的時候
縱向擴展
橫向擴容
第一條從前面引子開始就在舉例子了。后面兩個名詞用在這是我自己總結的。
縱向擴展,對一個對象有多個不同的操作,比如連接一個服務器、執行一條命令、上傳一個文件、斷開與服務器的連接。把這種對同一個對象執行的不同的操作寫在一個類里,每一種操作就是類里的一個函數
橫向擴展,原本有很多個函數都需要傳公共的參數的時候,可以都寫到一個類里。比如有很多個操作都需要和服務器交互,那么就都會需要地址、端口、密碼這些參數,然后不同的方法又需要不同的其他參數。每次定義函數以及之后調用函數都會重復的引用這幾個重復的參數。
# 面向過程定義3個函數,其中都會用到3個一樣的參數
def f1(host,port,pwd,arg1):
pass
def f2(host,port,pwd,arg1,arg2):
pass
def f3(host,port,pwd,arg1,arg2,arg3):
pass
# 調用的時候也要反復的來引用這些參數
f1(1,2,3,4)
f2(1,2,3,4,5)
f3(1,2,3,4,5,6)
# 面向對象來做同樣的事情,重復的參數寫到構造方法里
class Foo(object):
def __init__(self,host,port,pwd):
self.host = host
self.port = port
self.pwd = pwd
def f1(arg1):
pass
def f2(arg1,arg2):
pass
def f3(arg1,arg2,arg3):
pass
# 調用的時候先把重復的參數寫在一個對象里,然后可以分別調用這個對象的不同的方法
obj = Foo(1,2,3)
obj.f1(4)
obj.f2(4,5)
obj.f3(4,5,6)
類中的其他方法
類中的函數我們叫方法,默認在類中定義的函數都是保存在類中,要調用這個方法需要通過對象。叫做實例方法,就是必須是實例化之后才能使用的方法,之前都是這種實例方法。
靜態方法,就是一種普通函數,保存在類中,可以通過類來調用。使用裝飾器@staticmethod定義靜態方法。
class Foo(object):
def f1(self): # 這里的self參數是必須的
print("test in f1")
@staticmethod # 這個是靜態方法
def f2(): # 這里的參數不是必須的了
print("test in f2")
Foo.f2() # 并沒有創建對象,直接通過類調用了靜態方法,類似函數了
obj.f2() # 當然由于有繼承,通過對象也能夠調用類中的靜態方法
obj = Foo()
obj.f1()
上面的例子中,f1是實例方法,f2就是靜態方法。
這里的f1不要這么用,因為這個方法里并沒有用到對象里面的任何東西。但是這么寫,要調用f1必須得先實例化一個對象,但是其實并不需這個對象,而且還浪費空間。所以這種情況下,按照f2那樣定義成一個靜態方法會更好。
在別的純面向對象的語言里,也是提供靜態方法的。通過這種靜態方法,可以讓我們直接就能執行這個方法了。
另外除了靜態方法和實例方法,還有一個類方法,這個貌似和靜態方法在python里差不多,下面直接看看區別。
靜態方法 和 類方法的區別
類方法可以將類作為參數傳入,在繼承的時候,子類會優先調用子類的屬性和方法。
靜態方法,無法傳入類和對象,所以無論在哪個級別,永遠都是調用基類的屬性和方法
class Father(object):
test = 'This is in Father'
@classmethod
def test_classmethod(cls):
print(cls.test) # 類方法,可以將類作為參數
@staticmethod
def test_staticmethod():
print(Father.test) # 靜態方法,沒有參數傳入,只能用自己的類名,調用自己這個類的屬性
class Son(Father):
test = "This is in Son"
Father.test_classmethod()
Father.test_staticmethod()
Son.test_classmethod() # 繼承后的子類的類方法的結果不同了
Son.test_staticmethod()
類方法和靜態方法的作用就是可以通過類來調用類中的屬性和方法,而不需要先進行實例化。在還沒有實例化生成對象的時候,只能調用類中的這兩種方法。
類的組合關系
類與類之間除了可以是繼承關系,還可以有組合關系。比如有兩個類,學生和老師,那么可以在這之上在定義一個學校成員類,把學生和老師公有的屬性和方法放在學校成員類里,作為父類。而學生和老師就作為兩個子類繼承父類。這是是之前將的繼承,類于類之間是一種屬于的關系。
現在又有一個類,課程類。課程不屬于任何的人,但是老師有教授的課程,學生也有學習的課程。我們希望可以通過老師來獲得他所教授的課程,也就是獲得課程類的屬性和方法。所以課程和老師之間又需要有這某種關系,這是就需要用到組合關系。
假設一個老師只教一門課,先搞清楚組合關系,不考慮教多門課。
如果沒有組合關系,我們大概可以把課程的屬性作為老師的屬性,如下:
class Teacher(object):
def __init__(self,name,age,course_name,period,title):
"名字、年齡、課程名、課時、職稱"
self.name = name
self.age = age
self.course_name = course_name
self.period = period
self.title = title
當然我們還會有學生類,這會用到這些屬性,除了職稱。通過繼承關系我們可以把名字和年齡作為一個父類,讓老師和學生繼承,但是課程和課時就無法利用繼承關系實現代碼的重復使用了。
class Course(object):
def __init__(self,name,period):
"課程名、課時"
self.name = name
self.period = period
class People(object):
def __init__(self,name,age):
"名字、年齡"
self.name = name
self.age = age
class Teacher(People):
def __init__(self,name,age,title):
"名字、年齡、職稱"
super(Teacher,self).__init__(name,age)
self.title = title
class Student(People):
def __init__(self,name,age):
super(Student,self).__init__(name,age)
t1 = Teacher("Bob",32,"教授")
print(t1.name,t1.age,t1.title)
上面只是把繼承關系寫好了,單獨把課程定義為了一個類,但是并沒有把課程組合和別的類組合起來。
方法一:先把課程實例化,將實例化后的課程作為People類的一個屬性傳入
class Course(object):
def __init__(self,name,period):
"課程名、課時"
self.name = name
self.period = period
class People(object):
def __init__(self,name,age,course):
"名字、年齡、課程"
self.name = name
self.age = age
self.course = course
class Teacher(People):
def __init__(self,name,age,course,title):
super(Teacher,self).__init__(name,age,course)
self.title = title # 教師比學生多一個職稱屬性
class Student(People):
def __init__(self,name,age,course):
super(Student,self).__init__(name,age,course)
c1 = Course("python",360) # 先實例化一個課程
t1 = Teacher("Bob",32,c1,"教授") # 課程是教師的一個屬性
print(t1.course.name,t1.course.period) # 通過教師實例來調用課程類的屬性
方法二:不用實例化課程,而是在People類的構造方法里完成課程類的實例化。這樣需要在實例化教師的時候,將課程類的屬性一起傳入。
class Course(object):
def __init__(self,name,period):
"課程名、課時"
self.name = name
self.period = period
class People(object):
def __init__(self,name,age,course_name,period):
self.name = name
self.age = age
self.course = Course(course_name,period) # 在構造函數里完成實例化
class Teacher(People):
def __init__(self,name,age,course_name,period,title):
super(Teacher,self).__init__(name,age,course_name,period)
self.title = title
class Student(People):
def __init__(self,name,age,course_name,period):
super(Student,self).__init__(name,age,course)
t1 = Teacher("Bob",32,"python",360,"教授") # 需要將課程類的屬性在實例化的時候一起傳入
print(t1.course.name,t1.course.period) # 調用方法都是一樣的
組合同樣也是為了減少重復代碼。把另一個類的實例作為自己的屬性完成實例化(方法一),或者在實例化的時候同時完成了另一個類的實例化來用作自己的屬性(方法二)。之后就可以通過一個類來調用被組合的類的屬性和方法了。
上面只舉例了調用一個被組合的類的屬性,要使用方法也是一樣的。另外例子中老師和學生都是會上多門課的,這里只要引入數組的概念。這是之前學過的概念,關鍵是要把所有的知識點融會貫通。
作業:選課系統
角色:學校、講師、學員、課程
要求:
創建北京、上海 2 所學校
創建linux , python , go 3個課程 , linux\py 在北京開, go 在上海開
課程包含,周期,價格,通過學校創建課程
通過學校創建班級, 班級關聯課程、講師
創建學員時,選擇學校,關聯班級
創建講師角色時要關聯學校,
提供至少兩個角色接口
7.1 學員視圖, 可以注冊, 交學費, 選擇班級,
7.2 講師視圖, 講師可管理自己的班級, 上課時選擇班級, 查看班級學員列表 , 修改所管理的學員的成績
7.3 管理視圖,創建講師, 創建班級,創建課程
上面的操作產生的數據都通過pickle序列化保存到文件里
總結
以上是生活随笔為你收集整理的python自动化开发是什么_Python自动化开发学习6的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: css3宽度变大动画_学所 前端 | H
- 下一篇: docker镜像启动后端口号是多少_14