尚義住房和城鄉(xiāng)規(guī)劃建設(shè)局網(wǎng)站/友情鏈接聯(lián)盟
目錄
引入
一、函數(shù)
1、函數(shù)概述
2、參數(shù)和返回值
3、函數(shù)的調(diào)用
二、模塊
1、模塊概述
2、模塊應(yīng)用實(shí)例
三、類與對象
1、面向?qū)ο蟾攀?/p>
2、類
3、類的特點(diǎn)
引入
為了使程序?qū)崿F(xiàn)的代碼更加簡單。需要把程序分成越來越小的組成部分,3種方式——函數(shù)、對象、模塊。
一、函數(shù)
1、函數(shù)概述
C語言大括號{}里是函數(shù)體,python種縮進(jìn)塊里是函數(shù)體(配合def和冒號:)
函數(shù)外面定義的變量是全局變量(大寫配合下劃線)
函數(shù)內(nèi)部定義的局部變量(小寫配合下劃線),只在內(nèi)部有效
def foodprice(per_price,number):sum_price = per_price*numberreturn sum_price
PER_PRICE = float(input('請輸入單價(jià):'))
NUMBER = float(input('請輸入數(shù)量:'))
SUM_PRICE = foodprice(PER_PRICE,NUMBER)
print('一共',SUM_PRICE,'元')==========運(yùn)行結(jié)果==========
請輸入單價(jià):15
請輸入數(shù)量:4
一共 60.0 元
若想在函數(shù)內(nèi)部修改全局變量,并使之在整個(gè)程序生效用關(guān)鍵字global:
def foodprice(per_price,number):global PER_PRICEPER_PRICE = 10sum_price = per_price*numberreturn sum_price
2、參數(shù)和返回值
在python種,函數(shù)參數(shù)分為3種:位置參數(shù)、可變參數(shù)、關(guān)鍵字參數(shù)。參數(shù)的類型不同,參數(shù)的傳遞方式也不同。
①位置參數(shù)
位置參數(shù):傳入?yún)?shù)值按照位置順序依次賦給參數(shù)。
傳遞方式:直接傳入?yún)?shù)即可,如果有多個(gè)參數(shù),位置先后順序不可改變。若交換順序,函數(shù)結(jié)果則不同
def sub(x,y):return x-y
>>> sub(10,5)
5
>>> sub(5,10)
-5
②關(guān)鍵字參數(shù)
關(guān)鍵字參數(shù):通過參數(shù)名指定需要賦值的參數(shù),可忽略參數(shù)順序
傳遞方式:2種,一是直接傳入?yún)?shù),二是先將參數(shù)封裝成字典,再在封裝后的字典前添加兩個(gè)星號**傳入
>>> sub(y=5,x=10)
5
>>> sub(**{'y':5,'x':10})
5
③默認(rèn)值參數(shù)
當(dāng)函數(shù)調(diào)用忘記給某個(gè)參數(shù)值時(shí),會(huì)使用默認(rèn)值代替(以防報(bào)錯(cuò))
def sub(x=100,y=50):return x-y
>>> sub()
50
>>> sub(51)
1
>>> sub(10,5)
5
④可變參數(shù)
可變參數(shù):在定義函數(shù)參數(shù)時(shí),我們不知道到底需要幾個(gè)參數(shù),只要在參數(shù)前面加上星號*即可。
def var(*param):print('可變參數(shù)param中第3個(gè)參數(shù)是:',param[2])print('可變參數(shù)param的長度是:',len(param))
>>> var('BUCT',1958,'Liming',100,'A')
可變參數(shù)param中第3個(gè)參數(shù)是: Liming
可變參數(shù)param的長度是: 5
除了可變參數(shù),后也可有普通參數(shù)(普參需要用關(guān)鍵字參數(shù)傳值):
def var(*param,str1):print('可變參數(shù)param中第3個(gè)參數(shù)是:',param[2])print('可變參數(shù)param的長度是:',len(param))print('我是普通參數(shù):',str1)
>>> var('BUCT',1958,'Liming',100,'A',str1 = '普參')
可變參數(shù)param中第3個(gè)參數(shù)是: Liming
可變參數(shù)param的長度是: 5
我是普通參數(shù): 普參
⑤函數(shù)的返回值
若沒有用return指定返回值,則返回一個(gè)空None
result = var('BUCT',1958,'Liming',100,'A',str1 = '普參')
>>> print(result)
None
若將函數(shù)改為:
def var(*param,str1):print('可變參數(shù)param中第3個(gè)參數(shù)是:',param[2])print('可變參數(shù)param的長度是:',len(param))print('我是普通參數(shù):',str1)return param
>>> print(result)
('BUCT', 1958, 'Liming', 100, 'A')
3、函數(shù)的調(diào)用
自定義函數(shù):先定義再調(diào)用
內(nèi)置函數(shù):直接調(diào)用,有的在特定模塊里,需要先import相應(yīng)模塊
①嵌套調(diào)用
內(nèi)嵌函數(shù)/內(nèi)部函數(shù):在函數(shù)內(nèi)部再定義一個(gè)函數(shù),作用域只在其相鄰?fù)鈱雍瘮?shù)縮進(jìn)塊內(nèi)部。
②使用閉包
閉包:函數(shù)式編程的一個(gè)重要的語法結(jié)構(gòu),在一個(gè)內(nèi)部函數(shù)里對外部作用域(不是全局作用域)的變量進(jìn)行引用。此時(shí)這個(gè)內(nèi)部函數(shù)叫做閉包函數(shù),如下sub2(b)就是引用了a,為閉包函數(shù):
def sub(a):def sub2(b):result = a-breturn resultreturn sub2
print(sub(10)(5))
運(yùn)行結(jié)果:5
注:閉包本質(zhì)還是內(nèi)嵌函數(shù),不能再全局域訪問,外部函數(shù)sub的局部變量對于閉包來說是全局變量,可以訪問但是不能修改。
③遞歸調(diào)用
遞歸:嚴(yán)格說其屬于算法范疇,不屬于語法范圍。函數(shù)調(diào)用自身的行為叫做遞歸。
兩個(gè)條件:調(diào)用函數(shù)自身、設(shè)置了正確的返回條件。如計(jì)算正整數(shù)N的階乘:?
常規(guī)迭代算法:
def factorial(n):result = nfor i in range(1,n):result *= ireturn result
print(factorial(10))
運(yùn)行結(jié)果:3628800
迭代算法:
def factorial(n):if n == 1:return 1else:return n*factorial(n-1)
print(factorial(10))
運(yùn)行結(jié)果:3628800
注:python默認(rèn)遞歸深度為100層(限制),也可用sys.setrecursionlimit(x)指定遞歸深度。遞歸有危險(xiǎn)(消耗時(shí)間和空間),因?yàn)樗腔趶棗:统鰲2僮?#xff1b;遞歸忘記返回時(shí)/未設(shè)置正確返回條件時(shí),會(huì)使程序崩潰,消耗掉所有內(nèi)存。
二、模塊
1、模塊概述
模塊實(shí)際上是一種更為高級的封裝。前面容器(元組,列表)是對數(shù)據(jù)的封裝,函數(shù)是對語句的封裝,類是方法和屬性的封裝,模塊就是對程序的封裝?!褪俏覀儽4娴膶?shí)現(xiàn)特定功能的.py文件
命名空間:一個(gè)包含了一個(gè)或多個(gè)變量名稱和它們各自對應(yīng)的對象值的字典,python可調(diào)用局部命名空間和全局命名空間中的變量。
如果一個(gè)局部變量與全局變量重名,則再函數(shù)內(nèi)部調(diào)用局部變量時(shí),會(huì)屏蔽全局變量。
如果要修改函數(shù)內(nèi)全局變量的值,需要借助global
①模塊導(dǎo)入方法
方法一:
import modulename
import modulename1,modulename2
使用函數(shù)時(shí)modulename.functionname()即可
方法二:
from ?modulename ?import functionname1,functionname2
方法三:
import modulename as newname
相當(dāng)于給模塊起了個(gè)新名字,便于記憶也方便調(diào)用
②自定義模塊和包
自定義模塊:
編寫的mine.py文件放在與調(diào)用程序同一目錄下,在其他文件中使用時(shí)就可import mine使用。
自定義包:
在大型項(xiàng)目開發(fā)中,為了避免模塊名重復(fù),python引入了按目錄來組織模塊的方法,稱為包(package)。
包是一個(gè)分層級的文件目錄結(jié)構(gòu),定義了有模塊、子包、子包下的子包等組成的命名空間。
只要頂層報(bào)名不與其他人重名,內(nèi)部的所有模塊都不會(huì)沖突?!狿114
③安裝第三方包
pip install xxxx
2、模塊應(yīng)用實(shí)例
①日期和時(shí)間相關(guān):datatime模塊
導(dǎo)入:
from datetime import datetime
#不能import datetime,可以import datetime.datetime(因?yàn)閐atetime模塊里還有一個(gè)datetime類)
獲取當(dāng)前日期時(shí)間:
>>> now = datetime.now()
>>> print(now)
2023-10-05 17:28:24.303285
獲取指定日期時(shí)間:
>>> dt = datetime(2020,12,12,11,30,45)
>>> print(dt)
2020-12-12 11:30:45
datetime與timestamp互相轉(zhuǎn)換:
把1970-1-1 00:00:00 UTC+00:00的時(shí)間作為epoch time,記為0,當(dāng)前時(shí)間就是相對于epoch time的秒數(shù),稱為timestamp。
計(jì)算機(jī)中存儲(chǔ)的當(dāng)前時(shí)間是以timestamp表示的,與時(shí)區(qū)無關(guān),全球各地計(jì)算機(jī)在任意時(shí)刻的timestamp是相同的。
>>> dt = dt.timestamp()
>>> print(dt)
1607743845.0
#再轉(zhuǎn)換回去:
>>> dt = datetime.fromtimestamp(dt)
>>> print(dt)
2020-12-12 11:30:45
str轉(zhuǎn)換為datetime:
用戶輸入的日期和時(shí)間類型是字符串,要處理日期和時(shí)間,必須把str轉(zhuǎn)換為datetime
>>> test = datetime.strptime('2023-10-05 ?17:49:00','%Y-%m-%d %H:%M:%S') ?#特殊字符規(guī)定了格式
>>> print(test)
2023-10-05 17:49:00
datetime轉(zhuǎn)換為str:
若已有datetime對象,要把它格式化為字符才能顯示給用戶
>>> now = datetime.now()
>>> print(now.strftime('%a,%b %d %H:%M'))
Thu,Oct 05 17:28
datetime加減計(jì)算:(再導(dǎo)入timedelta類)
>>> from datetime import datetime,timedelta
>>> now = datetime.now()
>>> now
datetime.datetime(2023, 10, 5, 17, 58, 9, 377124)
>>> now + timedelta(hours = 2)
datetime.datetime(2023, 10, 5, 19, 58, 9, 377124)
>>> now - timedelta(days = 3)
datetime.datetime(2023, 10, 2, 17, 58, 9, 377124)
本地時(shí)間轉(zhuǎn)換為UTC時(shí)間:(再導(dǎo)入timedelta、timezone類)
本地時(shí)間為系統(tǒng)設(shè)定時(shí)區(qū)的時(shí)間,例如北京時(shí)間是UTC+8:00時(shí)區(qū)的時(shí)間,而UTC時(shí)間指UTC+0:00時(shí)區(qū)的時(shí)間。datetime里面有時(shí)區(qū)屬性tzinfo。
>>> from datetime import datetime,timedelta,timezone
>>> new_utc = timezone(timedelta(hours = 8)) #創(chuàng)建新時(shí)區(qū)UTC+8:00
>>> new_utc1 = timezone(timedelta(hours = 7)) #創(chuàng)建新時(shí)區(qū)UTC+7:00
>>> now = datetime.now()
>>> now
datetime.datetime(2023, 10, 5, 18, 9, 17, 667655)
>>> test = now.replace(tzinfo = new_utc) #為當(dāng)前時(shí)間強(qiáng)制設(shè)置新的時(shí)區(qū)
>>> test
datetime.datetime(2023, 10, 5, 18, 9, 17, 667655, tzinfo=datetime.timezone(datetime.timedelta(0, 28800)))
>>> test1 = now.replace(tzinfo = new_utc1) #為當(dāng)前時(shí)間強(qiáng)制設(shè)置新的時(shí)區(qū)UTC+7:00
>>> test1
datetime.datetime(2023, 10, 5, 18, 9, 17, 667655, tzinfo=datetime.timezone(datetime.timedelta(0, 25200)))
時(shí)區(qū)轉(zhuǎn)換:
先用datetime類提供的utcnow()方法獲取當(dāng)前UTC時(shí)間,再用astimezone()方法轉(zhuǎn)換為任意時(shí)區(qū)的時(shí)間
②讀寫JSON數(shù)據(jù):json模塊
JSON是一種輕量級的數(shù)據(jù)交換格式,等同于python里面的字典格式,里面可以包含方括號括起來的數(shù)組(列表)。json模塊專門解決json格式的數(shù)據(jù),提供4種方法:dumps()、dump()、loads()、load()
dumps()、dump()實(shí)現(xiàn)序列化功能:
dumps()實(shí)現(xiàn)將數(shù)據(jù)序列化為字符串str,而使用dump()時(shí)必須傳文件描述符,將序列化的字符串str保存到文件中。
>>> import json
>>> json.dumps('huang')
'"huang"'
>>> json.dumps(13.14)
'13.14'
>>> dict1 = {'name':'huang','school':'buct'}
>>> json.dumps(dict1)
'{"name": "huang", "school": "buct"}'>>> with open("D:\\json_test.json","w",encoding = 'utf-8')as file_test:json.dump(dict1,file_test,indent = 4)
運(yùn)行結(jié)果:dump()方法將字典數(shù)據(jù)dict_test保存到D盤文件夾下的json_test.json文件中,里面的內(nèi)容如下
{"name": "huang","school": "buct"
}
loads()、load()是反序列化方法:
loads()只完成了反序列化,load()只接收文件描述符,完成了讀取文件和反序列化。
>>> json.loads(json.dumps(dict1)) ?#loads直接操作程序中的字典
{'name': 'huang', 'school': 'buct'} ? #dumps將字典序列化成str,loads又使其恢復(fù)字典身份>>> with open("D:\\json_test.json","r",encoding = 'utf-8')as file_test: ?#讀剛才保存的序列化str字符串?dāng)?shù)據(jù)test_loads = json.loads(file_test.read()) ?#用loads實(shí)現(xiàn)反序列化file_test.seek(0) #重新定位在文件的第0位及開始位置test_load = json.load(file_test) ?#用load實(shí)現(xiàn)反序列化
>>> print(test_loads)
{'name': 'huang', 'school': 'buct'}
>>> print(test_load)
{'name': 'huang', 'school': 'buct'}
③系統(tǒng)相關(guān):sys模塊
sys是python自帶模塊,包含了與系統(tǒng)相關(guān)的信息??赏ㄟ^help(sys)或dir(sys)查看sys模塊的可用方法(很多),下面列舉幾種。
sys.path包含輸入模塊的目錄名列表:
>>> sys.path
['','D:\\python3.6.6\\Lib\\idlelib',
'D:\\python3.6.6\\python36.zip',
'D:\\python3.6.6\\DLLs',
'D:\\python3.6.6\\lib',
'D:\\python3.6.6',
'D:\\python3.6.6\\lib\\site-packages']
該命令獲取了指定模塊搜索路徑的字符串集合。將寫好的模塊放在上面得到的某個(gè)路徑下,就可以在使用import導(dǎo)入時(shí)正確找到,也可以用sys.path.append(自定義路徑)添加模塊路徑?!白远x模塊亂放程序是找不到的!”
sys.argv在外部向程序內(nèi)部傳遞參數(shù):
????
sys.argv變量是一個(gè)包含了命令行參數(shù)的字符串列表,利用命令行向程序傳遞參數(shù)。其中腳本的名稱是sys.argv列表的第一個(gè)參數(shù)。
④數(shù)學(xué):math模塊
math模塊也是python自帶模塊,包含了和數(shù)學(xué)運(yùn)算公式相關(guān)的信息——P125
⑤隨機(jī)數(shù):random模塊
列舉常用模塊:
生成隨機(jī)整數(shù)(需指定上下限,且下限小于上限):randint
import random
>>> random.randint(10,2390) ?#生成指定范圍內(nèi)的隨機(jī)整數(shù)
2375
生成隨機(jī)浮點(diǎn)數(shù):random
>>> random.random()
0.9935870033845187
>>> random.uniform(10,100)
27.07308173076904
>>> random.uniform(100,10)
18.198994262912336
隨機(jī)字符:choice
>>> random.choice('98%$333#@')
'3'
洗牌:shuffle
>>> test = ['a','B',1,2,5,'%']
>>> random.shuffle(test)
>>> print(test)
[5, 1, 2, 'a', 'B', '%']
三、類與對象
1、面向?qū)ο蟾攀?/h3>
面向?qū)ο缶幊?#xff08;Object Oriented Programming,OOP),是一種程序設(shè)計(jì)思想,是以建立模型體現(xiàn)出來的抽象思維過程和面向?qū)ο蟮姆椒ā?br /> 模型是用來反映現(xiàn)實(shí)世界中的事物特征的,是對事物特征和變化規(guī)律的抽象化,是更普遍、更集中、更深刻地描述客體的特征。
OOP把對象作為程序的基本單元,一個(gè)對象包含了數(shù)據(jù)和操作數(shù)據(jù)的函數(shù)。
術(shù)語簡介:
1、類:是創(chuàng)建對象的代碼段,描述了對象的特征、屬性、要實(shí)現(xiàn)的功能,以及采用的方法等。
2、屬性:描述了對象的靜態(tài)特征。
3、方法:描述了對象的動(dòng)態(tài)動(dòng)作。
4、對象:對象是類的一個(gè)實(shí)例,就是模擬真實(shí)事件,把數(shù)據(jù)和代碼段都集合到一起,即屬性、方法的集合。
5、實(shí)例:就是類的實(shí)體。
6、實(shí)例化:創(chuàng)建類的一個(gè)實(shí)例過程。
7、封裝:把對象的屬性、方法、事件集中到一個(gè)統(tǒng)一的類中,并對調(diào)用者屏蔽其中的細(xì)節(jié)。
8、繼承:一個(gè)類共享另一個(gè)類的數(shù)據(jù)結(jié)構(gòu)和方法的機(jī)制稱為繼承。起始類稱為基類、超類、父類,而繼承類稱為派生類、子類。繼承類是對被繼承類的拓展。
9、多態(tài):一個(gè)同樣的函數(shù)對于不同的對象可以具有不同的實(shí)現(xiàn)。
10、接口:定義了方法、屬性的結(jié)構(gòu),為其成員提供違約,不提供實(shí)現(xiàn)。不能直接從接口創(chuàng)建對象,必須首先創(chuàng)建一個(gè)類來實(shí)現(xiàn)接口所定義的內(nèi)容。
11、重載:一個(gè)方法可以具有許多不同的接口,但方法的名稱是相同的。
12、事件:事件是由某個(gè)外部行為所引發(fā)的對象方法。
13、重寫:在派生類中,對基類某個(gè)方法的程序代碼及進(jìn)行重新編碼,使其實(shí)現(xiàn)不同的功能。
14、構(gòu)造函數(shù):是創(chuàng)建對象所調(diào)用的特殊方法。
15、析構(gòu)函數(shù):是釋放對象時(shí)所調(diào)用的特殊方法。
2、類
①類的定義
類就是對象的屬性和方法的拼接,靜態(tài)的特征稱為屬性,動(dòng)態(tài)的動(dòng)作稱為方法。
>>> class Person: ?#規(guī)定類名以大寫字母開頭#屬性skincolor = "yellow"high = 185weight = 75#方法def goroad(self):print("人走路動(dòng)作的測試...")def sleep(self):print("睡覺,晚安!")
②類的使用(類實(shí)例化為對象)
>>> p = Person() ?#將類實(shí)例化為對象,注意后面要加括號()
③類的構(gòu)造方法及專有方法
類的構(gòu)造方法:__int__(self)。只要實(shí)例化一個(gè)對象,此方法就會(huì)在對象被創(chuàng)建時(shí)自動(dòng)調(diào)用——實(shí)例化對象時(shí)是可以傳入?yún)?shù)的,這些參數(shù)會(huì)自動(dòng)傳入__int__(self,param1,param2...)方法中,可以通過重寫這個(gè)方法來自定義對象的初始化操作。
>>> class Bear:def __init__(self,name):self.name = namedef kill(self):print("%s是保護(hù)動(dòng)物不可獵殺"%self.name)>>> a = Bear("狗熊")
>>> a.kill()
狗熊是保護(hù)動(dòng)物不可獵殺
解釋:與Person()相比,這里重寫了__init__()方法,不然默認(rèn)為__init__(self)。在Bear()中給了一個(gè)參數(shù)name,成了__init__(self,name),第一個(gè)參數(shù)self是默認(rèn)的,所以調(diào)用時(shí)把“狗熊”傳給了name。也可以給name默認(rèn)參數(shù),這樣即使忘記傳入?yún)?shù),程序也不會(huì)報(bào)錯(cuò):
>>> class Bear:def __init__(self,name = "狗熊"):self.name = namedef kill(self):print("%s是保護(hù)動(dòng)物不可獵殺"%self.name)>>> b = Bear()
>>> b.kill()
狗熊是保護(hù)動(dòng)物不可獵殺
>>> c = Bear("丹頂鶴")
>>> c.kill()
丹頂鶴是保護(hù)動(dòng)物不可獵殺
④類的訪問權(quán)限
在C++和JAVA中是通過關(guān)鍵字public、private來表明訪問權(quán)限是共有的還是私有的。在python中,默認(rèn)情況下對象的屬性和方法是公開的、公有的,通過點(diǎn)(.)操作符來訪問。如上面的kill()函數(shù)(方法)的訪問,也可以訪問變量:
>>> class Test:name = "大連橡塑">>> a = Test()
>>> a.name
'大連橡塑'
若變量前面加上雙下劃線(__)就表示聲明為私有變量就不可訪問:
>>> class Test:__name = "君欣旅店">>> a = Test()
>>> a.__name
Traceback (most recent call last):File "<pyshell#4>", line 1, in <module>a.__name
AttributeError: 'Test' object has no attribute '__name'
可利用函數(shù)方法訪問私有變量:
>>> class Test:__name = "君欣旅店"def getname(self):return self.__name>>> a = Test()
>>> a.getname()
'君欣旅店'
也可通過"_類名__變量名"格式訪問私有變量:
>>> a._Test__name
'君欣旅店'
(由此可見python的私有機(jī)制是偽私有,python的類是沒有權(quán)限控制的,變量可以被外界調(diào)用)
⑤獲取對象信息
類實(shí)例化對象后(如a = Test()),對象就可以調(diào)用類的屬性和方法:
即a.getname()、a.name、a.kill()這些
3、類的特點(diǎn)
①封裝
形式上看,對象封裝了屬性就是變量,而方法和函數(shù)是獨(dú)立性很強(qiáng)的模塊,封裝就是一種信息掩蔽技術(shù),使數(shù)據(jù)更加安全!
如列表實(shí)質(zhì)上是python的一個(gè)序列對象,sort()就是其中一個(gè)方法/函數(shù):
>>> list1 = ['E','C','B','A','D']
>>> list1.sort()
>>> list1
['A', 'B', 'C', 'D', 'E']
②多態(tài)
不同對象對同一方法響應(yīng)不同的行動(dòng)就是多態(tài)(內(nèi)部方法/函數(shù)名相同,但是不同類里面定義的功能不同):
>>> class Test1:def func(self):print("這是響應(yīng)1...")?? ??? ?
>>> class Test2:def func(self):print("這是響應(yīng)2...")?? ?
>>> x = Test1()
>>> y = Test2()
>>> x.func()
這是響應(yīng)1...
>>> y.func()
這是響應(yīng)2...
注意:self相當(dāng)于C++的this指針。由同一個(gè)類可以生成無數(shù)個(gè)對象,這些對象都源于同一個(gè)類的屬性和方法,當(dāng)一個(gè)對象的方法被調(diào)用時(shí),對象會(huì)將自身作為第一個(gè)參數(shù)傳給self參數(shù),接收self參數(shù)時(shí),python就知道是哪個(gè)對象在調(diào)用方法了。
③繼承
繼承是子類自動(dòng)共享父類數(shù)據(jù)和方法的機(jī)制。語法格式如下:
class ClassName(BaseClassName):
...
ClassName:子類名稱,第一個(gè)字母必須大寫。
BaseClassName/paraname:父類名稱。
子類可繼承父類的任何屬性和方法,如下定義類Test_list繼承列表list的屬性和方法(append和sort):
>>> class Test_list(list):pass>>> list1 = Test_list()
>>> list1.append('B')
>>> list1
['B']
>>> list1.append('UCT')
>>> list1
['B', 'UCT']
>>> list1.sort()
>>> list1
['B', 'UCT']
使用類繼承機(jī)制時(shí)的注意事項(xiàng):
a、若子類中定義與父類同名的方法或?qū)傩?#xff0c;自動(dòng)覆蓋父類里對應(yīng)的屬性或方法。
b、子類重寫父類中同名的屬性或方法,若被重寫的子類同名的方法里面沒有引入父類同名的方法,實(shí)例化對象調(diào)用父類的同名方法就會(huì)出錯(cuò):
import random
class Dog:def __init__(self):self.x = random.randint(1,100) ?#兩個(gè)縮進(jìn)self.y = random.randint(1,100)def run_Dog(self):self.x += 1print("狗狗的位置是:",self.x,self.y)class Dog1(Dog):passclass Dog2(Dog):def __init__(self):self.hungry = True ?def eat(self):if self.hungry:print("狗想吃東西了!")self.hungry = Falseelse:print("狗吃飽了!")調(diào)用:
>>> dog = Dog()
>>> dog.run_Dog()
狗狗的位置是: 62 69
>>> dog1 = Dog1()
>>> dog1.run_Dog()
狗狗的位置是: 29 89
>>> dog2 = Dog2()
>>> dog2.eat()
狗想吃東西了!
>>> dog2.eat()
狗吃飽了!
>>> dog2.run_Dog() ?#報(bào)錯(cuò)報(bào)錯(cuò)!!!
分析:子類Dog2重寫了父類(基類)Dog的構(gòu)造函數(shù)__init__(self),則父類構(gòu)造函數(shù)里的方法被覆蓋。要解決此問題,就要在子類里面重寫父類同名方法時(shí),先引入父類的同名方法,兩種技術(shù):a、調(diào)用未綁定的父類方法;b、使用super函數(shù)
a、調(diào)用未綁定的父類方法——語法格式:paraname.func(self)。父類名.方法名.(self)
對上面示例代碼的Dog2類更改:
def __init__(self):Dog.__init__(self) ? #加了這一行self.hungry = True ?
調(diào)用:
>>> dog2 = Dog2()
>>> dog2.run_Dog()
狗狗的位置是: 85 16
b、使用super函數(shù),該函數(shù)可以自動(dòng)找到父類方法和傳入的self參數(shù),語法格式:super().func([parameter])。parameter為可選參數(shù),若是self可省略。
對上面示例代碼的Dog2類更改:
def __init__(self):super().__init__() ?#加了這一行self.hungry = True調(diào)用:
>>> dog2 = Dog2()
>>> dog2.run_Dog()
狗狗的位置是: 96 82
使用super函數(shù)的方便之處在于不用寫任何關(guān)于基類(父類)的名稱,直接寫重寫的方法即可,會(huì)自動(dòng)去父類去尋找,尤其在多重繼承中,或者子類有多個(gè)祖先類時(shí),能自動(dòng)跳過多種層級去尋找。如果以后要更改父類,直接修改括號()里面的父類名稱即可,不用再修改重寫的同名方法里的內(nèi)容。
④多重繼承
一個(gè)子類同時(shí)繼承多個(gè)父類的屬性和方法:
class Classname(Base1,Base2,Base3):...
雖然多重繼承的機(jī)制可以使子類繼承多個(gè)屬性和方法,但是容易導(dǎo)致代碼混亂,引起不可預(yù)見的Bug,一般盡量避免使用。