python入門細節
相除后的類型
1
2
3
4
|
type ( 2 / 2 ) float type ( 2 / / 2 ) int |
雙斜杠是整除,出來的類型是int。單斜杠的出來的是float類型。
進制表示和轉換
進制表示:
二進制:0b 八進制:0o 十六進制:0x
進制轉換:
轉換為二進制:bin() 轉換為八進制:oct() 轉換為十進制:int() 轉換為十六進制:hex() 轉換為布爾類型:bool()
布爾類型
布爾類型轉換:bool()
布爾類型屬于數字這個基本數據類型里面只要是非零的數字,bool類型的值為True 對于字符串,布爾類型為True,除了空字符串 bool值為False:
bool(0) bool('') 中間沒有空格 bool([]) bool({}) bool(None) bool(NoneType) bool(set{})
多行字符串
三個引號可以再IDLE下回車不會執行換行。print函數可以輸出n這樣的反義字符。
單個引號想回車換行可以再前面加上字符即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
''' sadasdj adas ''' Out[ 1 ]: '\nsadasdj\nadas\n' 'asda File "<ipython-input-2-6af9d7d5e65d>" , line 1 'asda ^ SyntaxError: EOL while scanning string literal 'asdd\ adsad\ sad' Out[ 3 ]: 'asddadsadsad' print ( 'asda\nsada\n' ) asda sada ''' asda\n ''' Out[ 5 ]: '\nasda\n\n' |
原始字符串
原始字符串在print時只是輸出里面的字符,不考慮反義之類的,小心r大寫R都沒有關系。
1
2
3
4
5
6
7
8
|
print (r 'c:\nsda\nsds' ) c:\nsda\nsds print (r 'let' s go') File "<ipython-input-3-a81b31c0c433>" , line 1 print (r 'let' s go') ^ SyntaxError: invalid syntax |
字符串的運算
1.字符串的'+'和'*'
1
2
3
4
5
|
"hello" + "world" Out[ 1 ]: 'helloworld' "hello" * 3 Out[ 2 ]: 'hellohellohello' |
2.獲取字符串里的字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
"hello world" [ 0 ] Out[ 3 ]: 'h' "hello world" [ - 1 ] Out[ 4 ]: 'd' # 包括左面但是不包括右面 "hello world" [ 0 : 4 ] Out[ 5 ]: 'hell' "hello world" [ 0 : - 1 ] Out[ 6 ]: 'hello worl' # 超出長度時會按字符串最大的長度進行截取 "hello world" [ 0 : 20 ] Out[ 7 ]: 'hello world' # 沒有右邊的值的時候,表示直接輸出到末尾 "hello world" [ 6 :] Out[ 8 ]: 'world' # 負數在冒號前面的時候 "hello world" [ - 4 :] Out[ 9 ]: 'orld' |
python表示序列的方式 1.列表(list) 列表中的元素可以是任意類型的組合,比如列表的嵌套,布爾類型,字符串等等。
1.1 基本操作
1.1.1 基本選取(切片)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[ "新月打擊" , "蒼白之瀑" , "月之降臨" , "月神沖刺" ] Out[ 10 ]: [ '新月打擊' , '蒼白之瀑' , '月之降臨' , '月神沖刺' ] [ "新月打擊" , "蒼白之瀑" , "月之降臨" , "月神沖刺" ][ 0 ] Out[ 11 ]: '新月打擊' [ "新月打擊" , "蒼白之瀑" , "月之降臨" , "月神沖刺" ][ 0 : 2 ] Out[ 12 ]: [ '新月打擊' , '蒼白之瀑' ] [ "新月打擊" , "蒼白之瀑" , "月之降臨" , "月神沖刺" ][ - 1 :] Out[ 13 ]: [ '月神沖刺' ] a = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ] print (a[ 0 : 3 ]) print (a[ 0 : len (a): 2 ]) print (a[ len (a): 0 : - 2 ]) [ 1 , 2 , 3 ] [ 1 , 3 , 5 , 7 ] [ 8 , 6 , 4 , 2 ] #可以看到,切片操作很簡單,第二個冒號后面的數字可以看作是步長。注意負數時的用法。 |
可以看到,當沒有冒號的時候,單個選取出的是元素的類型。但是當有冒號的時候,選取出的是序列的類型。這里需要注意
1.1.2 列表的相加和乘法
1
2
3
4
5
|
[ "新月打擊" , "蒼白之瀑" , "月之降臨" , "月神沖刺" ] + [ '虛弱' , '點燃' ] Out[ 14 ]: [ '新月打擊' , '蒼白之瀑' , '月之降臨' , '月神沖刺' , '虛弱' , '點燃' ] [ '虛弱' , '點燃' ] * 3 Out[ 15 ]: [ '虛弱' , '點燃' , '虛弱' , '點燃' , '虛弱' , '點燃' ] |
1.1.3 判斷元素是否存在
運用in和not in即可
1
2
3
4
5
|
3 in [ 1 , 2 , 3 , 4 ] Out[ 21 ]: True 3 not in [ 1 , 2 , 3 , 4 ] Out[ 22 ]: False |
1.1.4 計算長度,最大小值
1
2
3
4
5
6
7
8
9
10
11
|
len ([ 1 , 2 , 3 ]) Out[ 23 ]: 3 len ( "hesadad" ) Out[ 24 ]: 7 max ([ 1 , 2 , 3 , 4 , 5 , 6 ]) Out[ 25 ]: 6 min ([ 1 , 2 , 3 , 4 ]) Out[ 26 ]: 1 |
1.1.5 append()
可以向列表中追加元素。
1
2
3
4
5
|
a = [ 1 , 2 , 3 , 4 ] a.append( '5' ) Out[ 22 ]: [ 1 , 2 , 3 , 4 , '5' ] |
2.元組(tuple) 元組的操作,包括訪問,加,乘,in等操作和列表是相同的。需要注意一點是:
1
2
3
4
5
6
7
8
|
type (( 1 )) Out[ 16 ]: int type (( 'sd' )) Out[ 17 ]: str type (( 1 , 2 , 3 )) Out[ 18 ]: tuple |
如果括號里有一個元素,默認為是一個運算,不會認為是元組的括號。如果要定義只有一個元素的元組的:
1
2
3
4
5
|
type (( 1 ,)) Out[ 19 ]: tuple # 括號里面什么都沒有表示一個空元組 type (()) Out[ 20 ]: tuple |
元組是序列,不可變類型,但是如果元組里包含了列表,比如:
1
2
3
4
5
6
|
a = ( 1 , 2 , 3 ,[ 4 , 5 ]) a[ 3 ][ 1 ] = '2' print (a) ( 1 , 2 , 3 , [ 4 , '2' ]) |
我們可以看到元組里的列表可以改變
3.字符串(str)
字符串和元組都是不可變的類型序列包括了字符串,列表和元組,序列都可以用下標索引和切片的方式。
set集合 set集合里的元素是無序的,不重復的。 in,not in,len,max,min,但是沒有加,乘這種操作。集合有相減,交集,并集等操作
1
2
3
4
5
6
7
8
|
{ 1 , 2 , 3 , 4 , 5 , 6 } - { 1 , 2 } Out[ 1 ]: { 3 , 4 , 5 , 6 } { 1 , 2 , 3 , 4 , 5 , 6 } & { 1 , 2 } Out[ 2 ]: { 1 , 2 } { 1 , 2 , 3 , 4 , 5 , 6 } | { 1 , 2 , 7 } Out[ 3 ]: { 1 , 2 , 3 , 4 , 5 , 6 , 7 } |
定義一個空集合的方法:set()
1
2
3
4
5
6
7
8
|
type ({}) Out[ 8 ]: dict type ( set ()) Out[ 9 ]: set len ( set ()) Out[ 10 ]: 0 |
字典(dict) 字典和集合類型(set)有些類似,里面是無序的,所以字典不是序列。字典中可以value可以使任意類型;但是key是可以的,key必須是不可變的類型,比如Int,str,tuple等,例如list就是不可以的。字典的訪問:{'key1':'value1,'key2':'value2'}['key1'],字典的訪問通過key來進行字典里,key值是不可以重復的,如果定義有重復雖然不會報錯,但是會自動選擇其中一個。
序列,集合和字典屬于組,是Python的基本數據類型。
變量 變量的定義時,首字母不能是數字,但可以是下劃線。字母,數組,下劃線可以組成變量。 Python 變量名區分大小寫。定義變量的時候不用指明類型,和C++不一樣。 值類型和引用類型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
a = 1 b = a a = 3 print (b) 1 a = [ 1 , 2 , 3 , 4 ] b = a a[ 0 ] = '1' print (b) [ '1' , 2 , 3 , 4 ] |
值類型:int str tuple(不可改變),在重新定義的時候,因為不可改變會生成一個新的對象,這個時候b仍然指向原對象,a指向了一個新對象
引用類型:list,set,dict(可以改變),在定義一個新對象的時候,會在原對象的基礎上進行改變而不產生新對象,所以無論是a還是b都會指向已經改變的原對象,所以a和b的值都會變化。
再進一步的,可以看以下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
a = 'hello' id (a) Out[ 15 ]: 1510080259608 a = a + 's' id (a) Out[ 17 ]: 1510081716832 a[ 0 ] = 's' Traceback (most recent call last): File "<ipython-input-18-02436d67df37>" , line 1 , in <module> a[ 0 ] = 's' TypeError: 'str' object does not support item assignment |
id()是查詢在計算機中的內存位置,我們可以看到發生了變化。所以a = a + 's'是可以的。但是對字符串的賦值操作,是不可以的,因為str是不可變的類型。
運算符 python中是沒有自增和自減這種操作的。表示“等于”是'==',“不等于”是'!=' 字符串相比較的時候,把字符串中每一個字符拿出來相比較,比較AscII碼值比較兩個列表,和字符串是相同的。元組也可以進行比較,和列表和字符串是相同的。非bool類型在參與邏輯運算的時候,比如int,float,str等類型,在參與and,or,not的時候,遵循的規則和c++中類似。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
0 and 1 Out[ 1 ]: 0 1 and 2 Out[ 2 ]: 2 1 and 0 Out[ 3 ]: 0 1 or 2 Out[ 4 ]: 1 0 or 1 Out[ 5 ]: 1 |
由上面的例子我們可以看出and和or的邏輯判斷規則和c++一致??兆址?,0等判斷為空,在上面的筆記中有記載。
成員運算符: in, not in
成員運算符表示一個元素是否在一個組里;成員運算符返回值類型是bool類型。在字典中,是判斷key值。
1
2
3
4
5
6
7
8
9
|
a = 1 a in { 1 : '1' } Out[ 7 ]: True a = '1' a in { 1 : '1' } Out[ 9 ]: False |
身份運算符
is, is not
身份運算符比較的是身份而不是值,簡單來說就是內存地址。和關系運算符“==”不一樣。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
a = 1 b = 1 a is b Out[ 12 ]: True id (a) Out[ 13 ]: 1438837200 id (b) Out[ 14 ]: 1438837200 b = 1.0 a is b Out[ 16 ]: False id (b) Out[ 17 ]: 2197963106536 a = = b Out[ 18 ]: True a = { 1 , 2 , 3 } b = { 2 , 1 , 3 } a = = b Out[ 21 ]: True a is b Out[ 22 ]: False c = ( 1 , 2 , 3 ) d = ( 2 , 1 , 3 ) c = = d Out[ 25 ]: False c is d Out[ 26 ]: False id (a) Out[ 27 ]: 2197907160424 id (b) Out[ 28 ]: 2197990760232 |
我們可以看到,在無序的set集合中,元素順序不一樣在內存中位置不同,雖然值相同但是身份仍然不一樣。
位運算符
&, |, ^, ~, <<, >>
以上都是把數字當作二進制進行運算。把數字按照二進制進行換算,以&舉例,相同位1,不同為0。然后再把二進制數轉換成數字原來的進制。eg: 2&3 == 2
判斷變量的類型
python中一切都是對象,對象有三大特征,值(value), 身份(id), 類型(type)。判斷變量的類型,可以使用isinstance()這個函數。
1
2
3
4
5
6
7
8
9
10
|
a = 'sds' isinstance (a, str ) Out[ 30 ]: True isinstance (a,( str , int , float )) Out[ 31 ]: True isinstance (a, int ) Out[ 32 ]: False |
isinstance可以判斷對象中的子類是否滿足條件,所以比較好。
vscode python 基本操作
+ 單行注釋:# 快捷鍵:ctrl + /
+ 多行注釋:""" """ 快捷鍵:alt + shift + a
pylint 每個文件(模塊)需要有開篇的注釋來說明作用。 python中不存在常量(constant)一說,但是對于形式上的常量,一般以全部大寫來表示 Python變量中兩個名字的銜接用下劃線,eg:test_account. python包和模塊注意事項 包和模塊是不會被重復導入的。盡量避免循環引入。導入一個模塊的時候,會執行這個模塊里面的代碼。
python中的普通模塊必須有一個包,當想要把一個可執行文件當作一個普通模塊運行時,可以使用-m參數,如:
python -m 命名空間.模塊名
注意:此處若當作普通模塊,必須包括包名/命名空間。python中可執行文件沒有所屬包。此外,當使用-m參數后,頂級包也相對改變。
dir函數
用來查看模塊或者類內部的變量,包括系統內置變量。
1
2
3
4
5
6
7
8
|
import sys infos = dir (sys) print (infos) [ '__displayhook__' , '__doc__' , '__excepthook__' , '__interactivehook__' , '__loader__' , '__name__' , '__package__' , '__spec__' , '__stderr__' , '__stdin__' , '__stdout__' , '_clear_type_cache' , '_current_frames' , '_debugmallocstats' , '_enablelegacywindowsfsencoding' ,......] # 可見打出了許多的變量,是sys模塊內部的變量。下面的代碼中也有應用,只不過沒有參數。 |
__name__的應用技巧
1
2
3
4
|
if __name__ = = '__main__' : pass #來判斷模塊是否被當作入口文件被調用,如果被當做模塊就不會打印if條件成立執行的語句,如果被當做入口文件才會執行 |
1.模塊導入的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 由父包引入子包或者同級別引入的情況 import module # 只能引入同一個包(不包括子包)里的模塊。注意這里不能直接引入模塊的變量。 import module as name # 使用的時候name.變量/函數等。 from packet import module # 可以父包里的子包引入模塊。 from packet.module import module.變量 / 函數等 from module import * # 引入module內__all__指定的變量/函數等。 from module import module.變量 1 , module.變量 2 ,...... # 引入多個變量可用逗號隔開 |
2.__init__.py
該文件,可以在導入一個包,或者導入包中的函數的時候,系統會首先執行該文件。
1
|
from packet import * # 這行代碼會引入被引入包中__init__.py中__all__指定的模塊。 |
3.模塊內置變量
1
2
3
4
5
6
7
8
|
a = 2 b = 1 infos = dir () print (infos) [ '__annotations__' , '__builtins__' , '__cached__' , '__doc__' , '__file__' , '__loader__' , '__name__' , '__package__' , '__spec__' , 'a' , 'b' ] # 上方除了'a','b'都是系統內置的變量。 |
下面介紹幾個比較重要的內置變量,利用import一個模塊的時候會執行該模塊里面的內容的機制。對于入口文件和模塊文件,內置變量的值有所不同。
模塊文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
''' this is a c3 doc ''' print ( "name: " + __name__) print ( "package: " + __package__) print ( "doc: " + __doc__) print ( "flie: " + __file__) import sub11.c3 PS D:\pycode\sub1> python c2.py name: sub11.c3 package: sub11 doc: this is a c3 doc flie: D:\pycode\sub1\sub11\c3.py # __doc__記錄的是該模塊的開頭注釋 # __name__記錄該模塊的名字 # __package__記錄該模塊屬于的包 # __file__記錄該模塊的文件的絕對路徑 |
入口文件
如果一個.py文件被當做一個應用程序的入口:
①它的名稱不再是本身的模塊名稱,而是被強制更改為__main__
②它不屬于任何包
③file內置變量不會像普通模塊一樣顯示絕對路徑,它所顯示的值也不是確定值,和執行命令所在目錄有關
注:python入口文件和普通導入的模塊文件是有差異的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
''' this is a c3 doc ''' print ( "name: " + __name__) print ( "package: " + ( __package__ or "當前模塊不屬于任何包" )) print ( "doc: " + __doc__) print ( "flie: " + __file__) name: __main__ package: 當前模塊不屬于任何包 doc: this is a c3 doc flie: c3.py # 該文件屬于sub11包(有__init__.py這個文件),但是我們是直接執行的c3.py文件,可見此時如果在c3.py中打印內置變量,__name__被強制定位__main__,而且package上也不會顯示出所屬于的包,file路徑也發生了變化。 |
4.絕對導入和相對導入
絕對導入是指從入口文件引入執行文件所在的文件夾的包中的模塊的時候,需要進行絕對導入。
相對導入指從模塊引入父級的模塊時,需要進行相對導入。
頂級包與入口文件main.py的位置有關,與main.py同級的包就是該包下所有模塊的頂級包。而對于入口文件來說不存在包的概念。
絕對導入/絕對路徑:從頂級包到被導入模塊名稱的完整路徑。
相對導入,一個'.'表示當前包,兩個'..'表示上一級包.'...'上上級包,以此類推
注意:
import不支持相對導入,只能使用from import格式實現相對導入。
入口文件中不能使用相對導入,因為它沒有包的概念。
使用相對導入不要超出頂級包///和入口文件同級都不能使用相對導入
函數注意事項 python默認有一個遞歸次數限制來防止無限遞歸調用,但可以設置遞歸最大次數:
1
2
3
|
import sys sys.setrecursionlimit( 10000 ) # 可以設置最大迭代10000次。不過理論上雖然設置這么多,但實際上仍然達不到允許迭代這么多次。 |
python中for循環內定義的變量可以在外部使用,這點和c和java不相同。若函數體中沒有返回值,則認為返回None。
1.return 返回多個值,鏈式賦值和序列解包
python函數返回多個值直接在return后面用逗號分隔要返回的值即可,返回結果是tuple元組類型。比較好的接收函數返回的多個值的方法不是用一個變量接收元組然后用序號訪問它的元素,而是直接用多個值接收然后分別使用這些變量,如:
1
2
3
4
5
6
7
|
def damage(skill1, skill2) damage1 = skll1 * 3 damage2 = skill2 * 3 + 10 return damage1, damage2 skill1_damage, skill2_damage = damage( 3 , 6 ) print (skill1_danage, skill2_damage) |
上面的接受參數的方式叫做序列解包。
鏈式賦值和序列解包
1
2
3
4
5
6
7
8
9
10
11
12
|
d = 1 , 2 , 3 print ( type (d)) a, b, c = d print (a, b, c) print ( '----------------' ) a = b = c = 1 print (a, b, c) < class 'tuple' > 1 2 3 - - - - - - - - - - - - - - - - 1 1 1 |
因為是序列解包,所以可以不是元組,列表也是可以的。最后如果多個變量取同一個值,那么可以用上面的方法來進行賦值。
2.函數參數
函數參數有:
必須參數:形參和實參。關鍵字參數默認參數可變參數可變參數可以解開可變,并且可以進行可變關鍵字參數。定義可變參數后,傳值的時候可以什么都不傳,這個時候是空元組或者空字典。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
def demo( * param): print (param) print ( type (param)) demo( 1 , 2 , 3 ,[ 4 , 5 , 6 ]) ( 1 , 2 , 3 , [ 4 , 5 , 6 ]) < class 'tuple' > # 傳入可變參數,會定義為元組。 def demo( * param): print (param) print ( type (param)) a = 1 , 2 , 3 demo(a) demo( * a) (( 1 , 2 , 3 ),) < class 'tuple' > ( 1 , 2 , 3 ) < class 'tuple' > # *可以解包。 def demo( * * param): print (param) print ( type (param)) demo(q = '萬能牌' , w = '切牌' , e = '占卜' ) { 'q' : '萬能牌' , 'w' : '切牌' , 'e' : '占卜' } < class 'dict' > # 可見傳進來以后是一個字典,很方便。這就是關鍵字可變參數。 def demo( * * param): print (param) print ( type (param)) for key,value in param.items(): print (key, ':' ,value,end = '|| ' ) demo(q = '萬能牌' , w = '切牌' , e = '占卜' ) { 'q' : '萬能牌' , 'w' : '切牌' , 'e' : '占卜' } < class 'dict' > q : 萬能牌|| w : 切牌|| e : 占卜|| # 傳入字典時可以采用上面的方式取出鍵值和內容。 def demo( * * param): print (param) print ( type (param)) for key,value in param.items(): print (key, ':' ,value,end = '|| ' ) a = { 'q' : '萬能牌' , 'w' : '切牌' , 'e' : '占卜' } demo( * * a) { 'q' : '萬能牌' , 'w' : '切牌' , 'e' : '占卜' } < class 'dict' > q : 萬能牌|| w : 切牌|| e : 占卜|| # 和傳入元組一樣,解序列可以傳入兩個*。 |
形參是定義函數的時候定義的參數,實參是調用函數的時候傳遞的參數。關鍵字參數通過指定形參來進行參數賦值??勺儏翟诒仨殔抵?,默認參數之前,否則會出現賦值的錯誤
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
def demo(param1,param2 = 2 , * param3): print (param1) print (param2) print (param3) demo( 'a' , 1 , 2 , 3 ) a 1 ( 2 , 3 ) # 可見如果默認參數在可變參數之前,會發生錯誤,和預想的(1,2,3)賦值給param3有區別。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # 調整一下順序可以得到想要的結果 def f1(name1, * args, name2 = '2' , * * kw): print (name1) print (name2) print (args) print (kw) f1( '1' , '3' , '4' ,a = '1' ,b = '2' ) 1 2 ( '3' , '4' ) { 'a' : '1' , 'b' : '2' } |
類注意事項 類名最好不要用下劃線,有多個單詞的時候可以采用大寫首字母的方法。類的最基本作用就是封裝。定義類,實例化對象類只負責定義和刻畫,并不負責去執行代碼。所以在類里面去執行方法是不正確的。在一個模塊里面,不要既定義類,又去實例化類執行代碼。不要把類和模塊搞混,類里面有自己的規則 1.構造函數 構造函數即__init__(self)
: 實例化類的時候構造函數被自動執行 構造函數返回值為None ,不能人為return更改??梢酝ㄟ^類名.__init__()
來執行構造函數。 2.類變量和實例變量 類變量是定義在類內但是不在__init__()
中;實例變量是定義在___init__()
中的。換句話說,實例變量是對象的,類變量是類的,二者不能混淆。 python中,類與對象的變量查找是有順序的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
class Student(): name = 'Catherian' age = 0 high = 170 def __init__( self , name, age, high): self .name = name self .age = age high = high # 注意這里的身高沒有用self.high來定義 def doHomework( self ): print ( 'doHomework' ) student1 = Student( '呵呵噠' , 18 , 180 ) student2 = Student( 'ojbk' , 16 , 175 ) print (student1.name, student1.age, student1.high) print (student2.name, student2.age, student2.high) print (Student.name, Student.age, Student.high) print (student1.__dict__) print (Student.__dict__) 呵呵噠 18 170 ojbk 16 170 Catherian 0 170 # 這里打印出的才是類變量 # 可以看到,盡管我們在實例化student1,student2的時候傳入了身高high這個數據,但是打印的時候我們發現輸出的是類變量high,并不是實例變量。 { 'name' : '呵呵噠' , 'age' : 18 } # __dict__對與對象,打印出的是對象的實例變量。可見里面并沒有high。所以實例變量是用 self. 來定義的。類的__dict__是打印出對象里面的內容,包括數據成員和方法,下面即是 { '__module__' : '__main__' , 'name' : 'Catherian' , 'age' : 0 , 'high' : 170 , '__init__' : <function Student.__init__ at 0x000001F06B70F9D8 >, 'doHomework' : <function Student.doHomework at 0x000001F06B70FA60 >, '__dict__' : <attribute '__dict__' of 'Student' objects>, '__weakref__' : <attribute '__weakref__' of 'Student' objects>, '__doc__' : None } |
雖然我們在實例化對象時傳入了數據,但是我們發現high是類變量不是實例變量,但是仍然sudent1.high打印出了變量,這是類變量而不是實例變量。這是因為,python中的查找機制:當查找實例變量不存在的時候,會繼續向上查找類中相對應的類變量,如果子類中沒有父類中有(出現繼承)時,會查找到父類。所以我們打印出了類變量。
3.實例方法 python中,實例方法的參數里必須要顯式的定義出self,但在外部調用的時候不需要給出self。self指的就是你在外部實例化的對象。所以self.
給出的是實例變量。 在實例方法中調用實例變量最好用self.
的形式來進行調用。因為你傳入的是形參. 在實例方法中調用類變量有兩種方法:類名.類變量
和self.__class__.類變量
4.類方法定義類方法:
1
2
3
4
5
6
7
|
class Student sum = 0 @classmethod def student_sum( cls ): pass # cls可以更換,函數上面是一個裝飾器,表示了這是一個類方法。cls換成self或者其他任意的都是可以的,和實例方法中self可以任意替換別的字符是一樣的。 |
對于類方法,調用類變量:cls.類變量
即可。所以cls代表的就是所屬于的類。在調用類方法的時候,可以 Student.studen_sum
也可以通過對象來調用,即:student1.student_sum
。但是建議用類名來調用比較好。
5.靜態方法定義靜態方法:
1
2
3
4
5
|
class Student sum = 0 @staticmethod def add(x,y) pass |
靜態方法上同樣要有裝飾器來修飾,但是函數中不用顯式的傳入self或者cls,更像是一個普通的函數。在類方法和靜態方法中都不能調用實例變量。一般不建議用靜態方法,類方法更方便。
6.成員可見性
在python中,實際上沒有什么是不能訪問的。成員可見性更像是一種標志,一切全靠自覺。定義私有變量或者方法的時候,只需要在變量或者方法前面加上雙下劃線就代表了私有(注意不要同時在后面再加上雙下劃線)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
# 我們定義一個學生類 class Student(): name = 'Catherian' age = 0 __high = 170 def __init__( self , name, age): self .name = name self .age = age self .__score = 0 def __doHomework( self ): print ( 'doHomework' ) student1 = Student( '呵呵噠' , 18 ) print (student1.__score) Traceback (most recent call last): File "c1.py" , line 15 , in <module> print (student1.__score) AttributeError: 'Student' object has no attribute '__score' # 可以看到不能這樣訪問。 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # 如果我們再加上一句再執行: student1 = Student( '呵呵噠' , 18 ) student1.__score = - 1 print (student1.__score) - 1 # 我們發現竟然可以成功賦值并且訪問的。這是因為 studen1.__score = -1 這個操作其實是又定義了一個新的實例變量。我們可以打印看一下 student1 = Student( 'okk' , 18 ) student1.__score = - 1 # print(student1.__score) print (student1.__dict__) { 'name' : 'okk' , 'age' : 18 , '_Student__score' : 0 , '__score' : - 1 } # 我們可以看到,我們再里面定義的__score變量被定義成了_Student__score變量,__score變量是我們根據Python動態特性新定義出的實例變量。所以要訪問私有變量也很簡單: print (student1._Student__score) 0 # 所以這個真的全靠自覺,python中沒什么是不能訪問的。 |
7.繼承性
python中可以單繼承也可以多繼承。
子類繼承父類,會繼承父類中的變量(類變量和實例變量都會繼承)和父類中的方法。在子類內部,如果有和父類重名的變量,會按照我們在類變量和實例變量
中說明的搜索規則進行。如果有重名的方法,想在子類內部進行調用,可以采用super(子類名, self)
進行調用父類重名函數。在python中,可以用類名調用實例方法。很奇怪但是是可以的。在子類構造函數中,通過傳入多個參數,調用父類構造函數即可完成初始化。多繼承容易出現BUG。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
# 定義一個Human父類 class Human(): sum = 0 def __init__( self , name, age): self .name = name self .age = age self .__class__. sum + = 1 def get_name( self ): print ( self .name) def do_Homework( self ): # 重名的方法 print ( "this is a parent method" ) # 定義一個子類Student from c2 import Human class Student(Human): sum = 0 # 和父類重名的類變量 def __init__( self , name, age, score): # 父類還有兩個參數,所以這里有三個參數 Human.__init__( self , name, age) # 這里注意通過類名調用方法,不能不加self self .score = score self .__class__. sum + = 1 def do_Homework( self ): # 和父類重名的方法。 super (Student, self ).do_Homework() print ( 'doHomework' ) student1 = Student( 'okk' , 18 , 61 ) print (student1. sum ) print (Student. sum ) print (student1.get_name()) print (student1.do_Homework()) 2 # 可見通過父類方法里對sum的操作,繼承到子類中時,對于重名變量仍然可以操作子類中的重名變量。 2 # 根據搜索機制,實例變量里沒有找到子類里的類變量,再沒有找父類。 okk None this is a parent method # 可見調用了父類中的方法。 doHomework None |
枚舉
python中的枚舉類型其實是一個類。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
from enum import Enum class diamond(Enum): # 必須要繼承父類Enum YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 print (diamond.YELLOW) diamond.YELLOW # 可見打印出的就是diamend.YELLOW |
在枚舉類型中,每個類型有不同的值,不允許出現相同類型賦不同值,值可以是任意類型的。如果出現了兩個枚舉類型的值相同,下面的枚舉類型會被當成是上面枚舉類型的別名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from enum import Enum class diamond(Enum): YELLOW = 1 BLUE = 1 GREEN = 3 RED = 4 print (diamond.BLUE) print (diamond.__members__.items()) # items()可以不需要。打印出所有的枚舉類型。 diamond.YELLOW # 可打印的是BLUE出來的是YEELOW。 odict_items([( 'YELLOW' , <diamond.YELLOW: 1 >), ( 'BLUE' , <diamond.YELLOW: 1 >), ( 'GREEN' , <diamond.GREEN: 3 >), ( 'RED' , <diamond.RED: 4 >)]) |
不能在類的外部修改類型的值,比如diamond.YELLOW = 5
是會報錯的。類型最好用大寫表示,表示為常量不能修改。枚舉類型,枚舉名稱,枚舉的值,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from enum import Enum class diamond(Enum): YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 print ( "枚舉類型為:" , type (diamond.GREEN), diamond.GREEN) print ( "枚舉的名稱為" , type (diamond.GREEN.name), diamond.GREEN.name) print ( "枚舉的值為:" , diamond.GREEN.value) 枚舉類型為: <enum 'diamond' > diamond.GREEN 枚舉的名稱為 < class 'str' > GREEN 枚舉的值為: 3 |
可以采用for循環獲得枚舉類型等:
1
2
3
4
5
6
7
|
for i in diamond: print (i) diamond.YELLOW diamond.BLUE diamond.GREEN diamond.RED |
枚舉類型之間不能做大小的比較,可以做等值的比較;枚舉類型和枚舉值之間不能做等值的比較;枚舉類型可以做身份(is)的比較。不同枚舉類之間的枚舉類型不能比較。從枚舉值獲得枚舉類型:
1
2
3
4
5
6
7
8
9
10
11
|
class diamond(Enum): YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 a = 1 print (diamond(a)) diamond.YELLOW # 從一個具體的值獲得相應的枚舉類型。很有用。 |
如果想要每個枚舉類型的值都是int類型,可以引入from enum import IntEnum
,在枚舉類的括號里為IntEnum
。如果不想出現兩個枚舉類型出現同一個值(會報錯),可以引入一個裝飾器:
1
2
3
4
5
6
7
8
9
|
from enum import Enum from enum import IntEnum, unique @unique # 裝飾器 class diamond(IntEnum): YELLOW = 1 BLUE = 2 GREEN = 3 RED = 4 |
原文鏈接:https://segmentfault.com/a/1190000014052535