Python入門細節

焱城發表於2019-02-16

python入門細節

相除後的型別

type(2/2)
float
type(2//2)
int

雙斜槓是整除,出來的型別是int。單斜槓的出來的是float型別。

進製表示和轉換

進製表示:

  • 二進位制:0b
  • 八進位制:0o
  • 十六進位制:0x

進位制轉換:

  • 轉換為二進位制:bin()
  • 轉換為八進位制:oct()
  • 轉換為十進位制:int()
  • 轉換為十六進位制:hex()
  • 轉換為布林型別:bool()

布林型別

布林型別轉換:bool()

  1. 布林型別屬於數字這個基本資料型別裡面
  2. 只要是非零的數字,bool型別的值為True
  3. 對於字串,布林型別為True,除了空字串
  4. bool值為False:
  • bool(0)
  • bool(“) 中間沒有空格
  • bool([])
  • bool({})
  • bool(None)
  • bool(NoneType)
  • bool(set{})

多行字串

三個引號可以再IDLE下回車不會執行換行。print函式可以輸出n這樣的反義字元。
單個引號想回車換行可以再前面加上字元即可。

```
sadasdj
adas
```
Out[1]: `
sadasdj
adas
`

`asda
  File "<ipython-input-2-6af9d7d5e65d>", line 1
    `asda
         ^
SyntaxError: EOL while scanning string literal


`asdd
adsad
sad`
Out[3]: `asddadsadsad`

print(`asda
sada
`)
asda
sada


```
asda

```
Out[5]: `
asda

`

原始字串

原始字串在print時只是輸出裡面的字元,不考慮反義之類的,小心r大寫R都沒有關係。

print(r`c:
sda
sds`)
c:
sda
sds

print(r`let`s go`)
  File "<ipython-input-3-a81b31c0c433>", line 1
    print(r`let`s go`)
                ^
SyntaxError: invalid syntax

字串的運算

1.字串的`+`和`*`

"hello"+"world"
Out[1]: `helloworld`

"hello"*3
Out[2]: `hellohellohello`

2.獲取字串裡的字元

"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 基本選取(切片)

["新月打擊","蒼白之瀑","月之降臨","月神衝刺"]
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 列表的相加和乘法

["新月打擊","蒼白之瀑","月之降臨","月神衝刺"]+[`虛弱`,`點燃`]
Out[14]: [`新月打擊`, `蒼白之瀑`, `月之降臨`, `月神衝刺`, `虛弱`, `點燃`]

[`虛弱`,`點燃`]*3
Out[15]: [`虛弱`, `點燃`, `虛弱`, `點燃`, `虛弱`, `點燃`]

1.1.3 判斷元素是否存在

  • 運用in和not in即可
3 in [1,2,3,4]
Out[21]: True

3 not in [1,2,3,4]
Out[22]: False

1.1.4 計算長度,最大小值

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()
可以向列表中追加元素。

a = [1,2,3,4]

a.append(`5`)

Out[22]: [1, 2, 3, 4, `5`]

2.元組(tuple)

  • 元組的操作,包括訪問,加,乘,in等操作和列表是相同的。
  • 需要注意一點是:
type((1))
Out[16]: int

type((`sd`))
Out[17]: str

type((1,2,3))
Out[18]: tuple

如果括號裡有一個元素,預設為是一個運算,不會認為是元組的括號。如果要定義只有一個元素的元組的:

type((1,))
Out[19]: tuple
# 括號裡面什麼都沒有表示一個空元組
type(())
Out[20]: tuple
  • 元組是序列,不可變型別,但是如果元組裡包含了列表,比如:
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} - {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()
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++不一樣。
  • 值型別和引用型別
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的值都會變化。

再進一步的,可以看以下程式碼:

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++中類似。
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值。

a = 1

a in {1:`1`}
Out[7]: True

a = `1`

a in {1:`1`}
Out[9]: False
  • 身份運算子

is, is not
身份運算子比較的是身份而不是值,簡單來說就是記憶體地址。和關係運算子“==”不一樣。

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()這個函式。

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函式

用來檢視模組或者類內部的變數,包括系統內建變數。

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__的應用技巧

if __name__ == `__main__`:
    pass

#來判斷模組是否被當作入口檔案被呼叫,如果被當做模組就不會列印if條件成立執行的語句,如果被當做入口檔案才會執行

1.模組匯入的方法

# 由父包引入子包或者同級別引入的情況

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

該檔案,可以在匯入一個包,或者匯入包中的函式的時候,系統會首先執行該檔案。

from packet import *  # 這行程式碼會引入被引入包中__init__.py中__all__指定的模組。

3.模組內建變數

a = 2
b = 1
infos = dir()
print(infos)

[`__annotations__`, `__builtins__`, `__cached__`, `__doc__`, `__file__`, `__loader__`, `__name__`, `__package__`, `__spec__`, `a`, `b`]

# 上方除了`a`,`b`都是系統內建的變數。

下面介紹幾個比較重要的內建變數,利用import一個模組的時候會執行該模組裡面的內容的機制。對於入口檔案和模組檔案,內建變數的值有所不同。

  • 模組檔案
```
this is a c3 doc
```
print("name: "+__name__)
print("package: "+__package__)
print("doc: "+__doc__)
print("flie: "+__file__)

import sub11.c3

PS D:pycodesub1> python c2.py
name: sub11.c3
package: sub11
doc:
this is a c3 doc

flie: D:pycodesub1sub11c3.py

# __doc__記錄的是該模組的開頭註釋

# __name__記錄該模組的名字

# __package__記錄該模組屬於的包

# __file__記錄該模組的檔案的絕對路徑
  • 入口檔案

如果一個.py檔案被當做一個應用程式的入口:
①它的名稱不再是本身的模組名稱,而是被強制更改為__main__
②它不屬於任何包
③file內建變數不會像普通模組一樣顯示絕對路徑,它所顯示的值也不是確定值,和執行命令所在目錄有關
注:python入口檔案和普通匯入的模組檔案是有差異的。

```
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.絕對匯入和相對匯入

匯入機制:

  • python模組匯入時的搜尋路徑:

    1. 程式主目錄,執行程式是包含執行程式碼檔案的目錄,互動模式下為當前工作目錄,優先順序最高
    2. PYTHONPATH中的目錄
    3. 標準連結庫目錄,就是python的安裝目錄,原始碼在裡面
    4. 3.x 中可以用.pth 檔案
  • 以上這些構成了sys.path。你寫的模組儲存路徑在sys.path 裡面就可以import。
  • 絕對匯入是指從入口檔案引入執行檔案所在的資料夾的包中的模組的時候,需要進行絕對匯入。from ... import...中如果沒有出現.模組名,也是絕對匯入。
  • 相對匯入指從模組引入父級的模組時,需要進行相對匯入。在入口檔案切記不能使用相對匯入,即帶.號。比如你單獨執行某個模組,但是在這個模組裡你用了相對匯入,那麼就會報錯。但是你在外面引入這個模組的時候是不存在問題的。
  • 注意在模組下面,最好不要有和模組名重名的檔案。
  1. 頂級包與入口檔案main.py的位置有關,與main.py同級的包就是該包下所有模組的頂級包。而對於入口檔案來說不存在包的概念。
  2. 絕對匯入/絕對路徑:從頂級包到被匯入模組名稱的完整路徑。注意一定是完整的路徑。
  3. 相對匯入,一個`.`表示當前包,兩個`..`表示上一級包.`…`上上級包,以此類推。

注意:

  • import不支援相對匯入,只能使用from import格式實現相對匯入。
  • 入口檔案中不能使用相對匯入,因為它沒有包的概念。
  • 使用相對匯入不要超出頂級包,和入口檔案同級都不能使用相對匯入
  • pycharm中,你開啟一個工程,你的入口檔案處的sys.path中會自動新增該工程中的目錄,所以假設你在模組a中相對引入其他模組b,模組b所在的位置超過了你現在所執行的檔案(入口檔案)所在的位置,但是隻要不超過當前的工程目錄所在的位置,仍然可以進行相對匯入。

函式

注意事項

  • python預設有一個遞迴次數限制來防止無限遞迴呼叫,但可以設定遞迴最大次數:
import sys
sys.setrecursionlimit(10000)
# 可以設定最大迭代10000次。不過理論上雖然設定這麼多,但實際上仍然達不到允許迭代這麼多次。
  • python中for迴圈內定義的變數可以在外部使用,這點和c和java不相同。
  • 若函式體中沒有返回值,則認為返回None。

1.return 返回多個值,鏈式賦值和序列解包

python函式返回多個值直接在return後面用逗號分隔要返回的值即可,返回結果是tuple元組型別。比較好的接收函式返回的多個值的方法不是用一個變數接收元組然後用序號訪問它的元素,而是直接用多個值接收然後分別使用這些變數,如:

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)

上面的接受引數的方式叫做序列解包。

  • 鏈式賦值和序列解包
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.函式引數

函式引數有:

  • 必須引數:形參和實參。
  • 關鍵字引數
  • 預設引數
  • 可變引數
  • 可變引數可以解開可變,並且可以進行可變關鍵字引數。定義可變引數後,傳值的時候可以什麼都不傳,這個時候是空元組或者空字典。
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 : 占卜||

# 和傳入元組一樣,解序列可以傳入兩個*。
  • 形參是定義函式的時候定義的引數,實參是呼叫函式的時候傳遞的引數。
  • 關鍵字引數通過指定形參來進行引數賦值。
  • 可變引數在必須引數之後,預設引數之前,否則會出現賦值的錯誤
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中,類與物件的變數查詢是有順序的。
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.類方法

  • 定義類方法:
class Student
    sum = 0
    @classmethod
    def student_sum(cls):
        pass
    
# cls可以更換,函式上面是一個裝飾器,表示了這是一個類方法。cls換成self或者其他任意的都是可以的,和例項方法中self可以任意替換別的字元是一樣的。

對於類方法,呼叫類變數:cls.類變數 即可。所以cls代表的就是所屬於的類。在呼叫類方法的時候,可以 Student.studen_sum 也可以通過物件來呼叫,即:student1.student_sum。但是建議用類名來呼叫比較好。

5.靜態方法

  • 定義靜態方法:
class Student
    sum = 0
    @staticmethod
    def add(x,y)
        pass

靜態方法上同樣要有裝飾器來修飾,但是函式中不用顯式的傳入self或者cls,更像是一個普通的函式。在類方法和靜態方法中都不能呼叫例項變數。一般不建議用靜態方法,類方法更方便。

6.成員可見性

在python中,實際上沒有什麼是不能訪問的。成員可見性更像是一種標誌,一切全靠自覺。定義私有變數或者方法的時候,只需要在變數或者方法前面加上雙下劃線就代表了私有(注意不要同時在後面再加上雙下劃線)

# 我們定義一個學生類

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。
# 定義一個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中的列舉型別其實是一個類。

from enum import Enum

class diamond(Enum):  # 必須要繼承父類Enum
    YELLOW = 1
    BLUE = 2
    GREEN = 3
    RED = 4
print(diamond.YELLOW)


diamond.YELLOW

# 可見列印出的就是diamend.YELLOW
  • 在列舉型別中,每個型別有不同的值,不允許出現相同型別賦不同值,值可以是任意型別的。如果出現了兩個列舉型別的值相同,下面的列舉型別會被當成是上面列舉型別的別名。
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是會報錯的。
  • 型別最好用大寫表示,表示為常量不能修改。
  • 列舉型別,列舉名稱,列舉的值,程式碼如下:
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迴圈獲得列舉型別等:
for i in diamond:
    print(i)
    
diamond.YELLOW
diamond.BLUE
diamond.GREEN
diamond.RED
  • 列舉型別之間不能做大小的比較,可以做等值的比較;列舉型別和列舉值之間不能做等值的比較;列舉型別可以做身份(is)的比較。不同列舉類之間的列舉型別不能比較。
  • 從列舉值獲得列舉型別:
class diamond(Enum):
    YELLOW = 1
    BLUE = 2
    GREEN = 3
    RED = 4
a = 1
print(diamond(a))


diamond.YELLOW
# 從一個具體的值獲得相應的列舉型別。很有用。
  • 如果想要每個列舉型別的值都是int型別,可以引入from enum import IntEnum,在列舉類的括號裡為IntEnum
  • 如果不想出現兩個列舉型別出現同一個值(會報錯),可以引入一個裝飾器:
from enum import Enum
from enum import IntEnum, unique

@unique # 裝飾器
class diamond(IntEnum):
    YELLOW = 1
    BLUE = 2
    GREEN = 3
    RED = 4

相關文章