Python learning

Python learning

編碼

    # -*- coding: utf-8 -*-

計(jì)算

Python 支持的數(shù)字類型有:int、float、Decimal(十進(jìn)制)、Fraction(分?jǐn)?shù))、復(fù)數(shù)

    >>> 8 / 5
    1.6
    >>> 17 // 5
    3
    >>> 5 * 2
    10
    >>> 5 ** 2
    25
    >>> 3.5 // 1.7
    2.0

字符串

可以單雙引號(hào),特殊字符使用反斜杠轉(zhuǎn)義。

特殊的一些:

  1. 如果不想讓反斜杠當(dāng)做轉(zhuǎn)義輸出,可以用原始字符串,方法是在第一個(gè)引號(hào)前面加上一個(gè)r,r"C:\some\name"
  2. 字符串文本能夠分成多行。一種方法是使用三引號(hào): """...""" 或者 '''...'''。行尾換行符會(huì)被自動(dòng)包含到字符串中,但是可以在行尾加上 \ 來避免這個(gè)行為(行尾加上則不換行)
  3. 字符串可以由 + 操作符連接(粘到一起),可以由 * 表示重復(fù),3 * 'un' + 'ium' == 'unununium'
  4. 字符串也可以被截取,索引從0記起,如word[2];索引也可以是負(fù)數(shù),這將導(dǎo)致從右邊開始計(jì)算,word[-1]表示最后一個(gè)字符
  5. 字符串還支持切片,如word[2:5]表示獲取位置[2,5)的三個(gè)字符,如果word是Python,則獲取的字符串是tho,word[:5]表示從最開始到第4個(gè)位置的字符串,切片也支持負(fù)數(shù)
  6. 索引過大會(huì)引發(fā)IndexError,但是切片過大不會(huì)報(bào)錯(cuò)
  7. Python字符串不可以被更改 — 它們是 不可變的 (整體賦值是可以的)。因此,賦值給字符串索引的位置會(huì)導(dǎo)致錯(cuò)誤。word[1] = 'j'是錯(cuò)誤的!而word = 'aaa'可以

相關(guān)鏈接:

  1. Text Sequence Type — str
  2. String Methods: 轉(zhuǎn)換和查找
  3. String Formatting
  4. String Formatting Operations

列表

同樣支持索引和切片、所有的切片操作都會(huì)返回一個(gè)包含請(qǐng)求的元素的新列表。這意味著下面的切片操作返回列表一個(gè)新的(淺)拷貝副本

    >>> sq1 = [1, 2, 3]
    >>> sq2 = sq1
    >>> sq3 = sq1[:]
    >>> sq1
    [1, 2, 3]
    >>> sq2
    [1, 2, 3]
    >>> sq3
    [1, 2, 3]
    >>> sq2[1] = 10
    >>> sq1
    [1, 10, 3]
    >>> sq2
    [1, 10, 3]
    >>> sq3[1] = 100
    >>> sq1
    [1, 10, 3]
    >>> sq3
    [1, 100, 3]

所以,我們知道,要想復(fù)制一個(gè)列表,需要使用[:]來獲取淺拷貝副本,而不能直接賦值。從上面的例子,也可以知道,列表支持對(duì)每個(gè)元素單獨(dú)修改

所以,還可以這樣:

    >>> sq1
    [1, 10, 3]
    >>> sq1 + [1, 2]
    [1, 10, 3, 1, 2]
    >>> sq1
    [1, 10, 3]
    >>> sq1.append(10)
    >>> sq1
    [1, 10, 3, 10]
    >>> sq1.extend([7, 6, 5])
    >>> sq1
    [1, 10, 3, 10, 7, 6, 5]
    >>> # insert value
    ... sq1[1:1] = [40, 50]
    >>> sq1
    [1, 40, 50, 10, 3, 10, 7, 6, 5]
    >>> # replace value
    ... sq1[1:3] = [60, 70]
    >>> sq1
    [1, 60, 70, 10, 3, 10, 7, 6, 5]
    >>> # remove value
    ... sq1[1:3] = []
    >>> sq1
    [1, 10, 3, 10, 7, 6, 5]

一個(gè)例子

    >>> # fibonacci
    ... a, b = 0, 1
    >>> while b < 10:
    ...     print(b)
    ...     a, b = b, a+b
    ...
    1
    1
    2
    3
    5
    8

這里要注意的是:

注意a, b = 0, 1a, b = b, a+b,這是 多重賦值 ,變量在賦值前,右邊的表達(dá)式從左到右計(jì)算,然后再分別賦值給左邊的每個(gè)變量。所以,將第二個(gè)多重賦值改成b, a = a+b, b也沒有問題

條件判斷:0、空序列(長(zhǎng)度為0就算)為false,標(biāo)準(zhǔn)比較操作符與 C 相同: < , >==<=>=!=

print()函數(shù)可以用,隔開變量輸出,并自動(dòng)會(huì)在兩個(gè)變量之間加一個(gè)空格;print('aaa', end=','),end的默認(rèn)值是\n,改掉這個(gè)之后,print不換行,而是以,結(jié)尾

流程控制

if語句

    if x < 0:
        print(x)
    elif x == 0:
        print("hello")
    else:
        print("+")

for語句

    words = ['cat', 'window', 'defenestrate']
    for w in words:
        print(w, len(w))

這里需要注意的是,Python 的 for 語句依據(jù)任意序列(鏈表或字符串)中的子項(xiàng),按它們?cè)谛蛄兄械捻樞騺磉M(jìn)行迭代。所以,在迭代過程中修改迭代序列不安全。如果你想要修改你迭代的序列(例如,復(fù)制選擇項(xiàng)),你可以迭代它的復(fù)本

    for w in words[:]:
        if len(w) > 6:
            words.insert(0, w)
    print(words)
    # ['defenestrate', 'cat', 'window', 'defenestrate']

在循環(huán)(for, while)中同樣可以使用breakcontinue,作用和 C 中相同

循環(huán)中,支持else子句,作用是在循環(huán)正常結(jié)束(break不算)后執(zhí)行

range()函數(shù)

用于生成一個(gè)等差級(jí)數(shù)鏈表(注意:這不是列表List,是迭代器),以下是例子:

    >>> for i in range(5):
    ...     print(i)
    ...
    0
    1
    2
    3
    4

    range(5, 10)
       5 through 9

    range(0, 10, 3)
       0, 3, 6, 9

    range(-10, -100, -30)
      -10, -40, -70

    >>> a = ['Mary', 'had', 'a', 'little', 'lamb']
    >>> for i in range(len(a)):
    ...     print(i, a[i])
    ...
    0 Mary
    1 had
    2 a
    3 little
    4 lamb

    >>> print(range(10))
    range(0, 10)

    >>> list(range(5))
    [0, 1, 2, 3, 4]

定義函數(shù)

關(guān)鍵字def引入一個(gè)函數(shù)定義,在其后必須跟有函數(shù)名和包括形式參數(shù)的圓括號(hào)

函數(shù)體的第一行語句可以是可選的字符串文本,這個(gè)字符串是函數(shù)的文檔字符串,或者稱為 docstring。有些工具通過 docstrings 自動(dòng)生成在線的或可打印的文檔,或者讓用戶通過代碼交互瀏覽;在你的代碼中包含 docstrings 是一個(gè)好的實(shí)踐,讓它成為習(xí)慣吧

    def func():
        """ 這是一個(gè)docstring """
        ...

沒有return語句的函數(shù)會(huì)返回None

在函數(shù)中引用全局的變量,可以用global語句聲明,如:global a

函數(shù)參數(shù)

默認(rèn)參數(shù)值

    def func(num, str="hello"):
        if num in (1, 2, 3):
            print(num)

重要警告:默認(rèn)值只被賦值一次。這使得當(dāng)默認(rèn)值是可變對(duì)象時(shí)會(huì)有所不同,比如列表、字典或者大多數(shù)類的實(shí)例。例如,下面的函數(shù)在后續(xù)調(diào)用過程中會(huì)累積(前面)傳給它的參數(shù)!!

    def f(a, L=[]):
        L.append(a)
        return L

    print(f(1))
    print(f(2))
    print(f(3))

會(huì)輸出:

    [1]
    [1, 2]
    [1, 2, 3]

取而代之的方法:

    def f(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L

關(guān)鍵字參數(shù)

    def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
        print("-- This parrot wouldn't", action, end=' ')
        print("if you put", voltage, "volts through it.")
        print("-- Lovely plumage, the", type)
        print("-- It's", state, "!")

    # 接受一個(gè)必選參數(shù) (voltage) 以及三個(gè)可選參數(shù) (state, action, 和 type)。可以用以下的任一方法調(diào)用

    parrot(1000)                                          # 1 positional argument
    parrot(voltage=1000)                                  # 1 keyword argument
    parrot(voltage=1000000, action='VOOOOOM')             # 2 keyword arguments
    parrot(action='VOOOOOM', voltage=1000000)             # 2 keyword arguments
    parrot('a million', 'bereft of life', 'jump')         # 3 positional arguments
    parrot('a thousand', state='pushing up the daisies')  # 1 positional, 1 keyword

可變參數(shù)列表

一個(gè)最不常用的選擇是可以讓函數(shù)調(diào)用可變個(gè)數(shù)的參數(shù)。這些參數(shù)被包裝進(jìn)一個(gè)元組。在這些可變個(gè)數(shù)的參數(shù)之前,可以有零到多個(gè)普通的參數(shù)

    def write_multiple_items(file, separator, *args):
        file.write(separator.join(args))

通常,這些可變參數(shù)是參數(shù)列表中的最后一個(gè)(只針對(duì)于非關(guān)鍵字參數(shù)),因?yàn)樗鼈儗阉械氖S噍斎雲(yún)?shù)傳遞給函數(shù)。任何出現(xiàn)在*args后的參數(shù)是關(guān)鍵字參數(shù),這意味著,他們只能被用作關(guān)鍵字,而不是位置參數(shù)

    >>> def concat(*args, sep="/"):
    ...    return sep.join(args)
    ...
    >>> concat("earth", "mars", "venus")
    'earth/mars/venus'
    >>> concat("earth", "mars", "venus", sep=".")
    'earth.mars.venus'

可變關(guān)鍵字參數(shù)列表

相當(dāng)于接收一個(gè)字典,必須在可變參數(shù)列表(如果有)的后面

    def cheeseshop(kind, *arguments, **keywords):
        print("-- Do you have any", kind, "?")
        print("-- I'm sorry, we're all out of", kind)
        for arg in arguments:
            print(arg)
        print("-" * 40)
        keys = sorted(keywords.keys())
        for kw in keys:
            print(kw, ":", keywords[kw])

    cheeseshop("Limburger", "It's very runny, sir.",
               "It's really very, VERY runny, sir.",
               shopkeeper="Michael Palin",
               client="John Cleese",
               sketch="Cheese Shop Sketch")

    """ result:
    -- Do you have any Limburger ?
    -- I'm sorry, we're all out of Limburger
    It's very runny, sir.
    It's really very, VERY runny, sir.
    ----------------------------------------
    client : John Cleese
    shopkeeper : Michael Palin
    sketch : Cheese Shop Sketch
    """

在傳遞可變參數(shù)的時(shí)候可能遇到一種情況是,當(dāng)要傳遞的參數(shù)已經(jīng)是一個(gè)列表,而函數(shù)需要的是可變長(zhǎng)參數(shù),可以使用*來拆開。同理,字典拆成關(guān)鍵字參數(shù)可以用**

    >>> args = [3, 6]
    >>> list(range(*args))
    [3, 4, 5]
    >>> list(range(3, 6))
    [3, 4, 5]

Lambda形式

通過lambda關(guān)鍵字,可以創(chuàng)建短小的匿名函數(shù)。這里有一個(gè)函數(shù)返回它的兩個(gè)參數(shù)的和: lambda a, b: a+b

lambda 形式可以從外部作用域引用變量:

    >>> def make_incrementor(n):
    ...     return lambda x: x + n
    ...
    >>> f = make_incrementor(42)
    >>> f(0)
    42
    >>> f(1)
    43

pass語句

pass 語句什么也不做。它用于那些語法上必須要有什么語句,但程序什么也不做的場(chǎng)合

    class MyCls:
        pass
    def func():
        pass
    while True:
        pass

各種數(shù)據(jù)結(jié)構(gòu)

列表方法

    >>> a = [66.25, 333, 333, 1, 1234.5]
    >>> print(a.count(333), a.count(66.25), a.count('x'))
    2 1 0
    >>> a.insert(2, -1)
    >>> a.append(333)
    >>> a
    [66.25, 333, -1, 333, 1, 1234.5, 333]
    >>> a.index(333)
    1
    >>> a.remove(333)
    >>> a
    [66.25, -1, 333, 1, 1234.5, 333]
    >>> a.reverse()
    >>> a
    [333, 1234.5, 1, 333, -1, 66.25]
    >>> a.sort()
    >>> a
    [-1, 1, 66.25, 333, 333, 1234.5]
    >>> a.pop()
    1234.5
    >>> a
    [-1, 1, 66.25, 333, 333]

可以看到,列表自帶pop和append方法,所以,可以直接把列表當(dāng)做堆棧使用

    >>> stack = [3, 4, 5]
    >>> stack.append(6)
    >>> stack.append(7)
    >>> stack
    [3, 4, 5, 6, 7]
    >>> stack.pop()
    7
    >>> stack
    [3, 4, 5, 6]
    >>> stack.pop()
    6
    >>> stack.pop()
    5
    >>> stack
    [3, 4]

使用隊(duì)列:如果把列表當(dāng)做隊(duì)列來使用,效率不高(頭部插入和彈出很慢)??梢允褂?code>collections.deque

    >>> from collections import deque
    >>> queue = deque(["Eric", "John", "Michael"])
    >>> queue.append("Terry")       # Terry arrives
    >>> queue.append("Graham")      # Graham arrives
    >>> queue.popleft()             # The first to arrive now leaves
    'Eric'
    >>> queue.popleft()             # The second to arrive now leaves
    'John'
    >>> queue                       # Remaining queue in order of arrival
    deque(['Michael', 'Terry', 'Graham'])

列表推導(dǎo)式為從序列中創(chuàng)建列表提供了一個(gè)簡(jiǎn)單的方法

常規(guī)的方法如下,副作用是x變量在循環(huán)完畢后依然存在,而且長(zhǎng)...

    >>> squares = []
    >>> for x in range(10):
    ...     squares.append(x**2)
    ...
    >>> squares
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

使用列表推導(dǎo)式:

    >>> squares = [x ** 2 for x in range(10)]
    >>> squares
    [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

列表推導(dǎo)式由包含一個(gè)表達(dá)式的括號(hào)組成,表達(dá)式后面跟隨一個(gè)for子句,之后可以有零或多個(gè)forif子句。結(jié)果是一個(gè)列表,由表達(dá)式依據(jù)其后面的forif子句上下文計(jì)算而來的結(jié)果構(gòu)成

另一個(gè)例子:

    >>> [(x, y) for x in [1,2,3] for y in [3,1,4] if x != y]
    [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

更多例子!!

del語句

del語句可以刪除列表的對(duì)應(yīng)的索引項(xiàng) 或 變量,與pop()方法的區(qū)別在于不返回值,并且可以刪除切片和整個(gè)變量

    >>> a = [-1, 1, 66.25, 333, 333, 1234.5]
    >>> del a[0]
    >>> a
    [1, 66.25, 333, 333, 1234.5]
    >>> del a[2:4]
    >>> a
    [1, 66.25, 1234.5]
    >>> del a[:]
    >>> a
    []
    >>> del a
    >>> a
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'a' is not defined

元組和序列

我們知道列表和字符串有很多通用的屬性,例如索引和切割操作。它們是序列類型中的兩種

元組也是一種標(biāo)準(zhǔn)序列類型,一個(gè)元組由數(shù)個(gè)逗號(hào)分隔的值組成:

    >>> t = 12345, 54321, 'hello!'
    >>> t[0]
    12345
    >>> t
    (12345, 54321, 'hello!')
    >>> # Tuples may be nested:
    ... u = t, (1, 2, 3, 4, 5)
    >>> u
    ((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
    >>> # Tuples are immutable:
    ... t[0] = 88888
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    >>> # but they can contain mutable objects:
    ... v = ([1, 2, 3], [3, 2, 1])
    >>> v
    ([1, 2, 3], [3, 2, 1])

上面的例子說明,元組在輸出的時(shí)候總是有括號(hào)的,但是輸入的時(shí)候可以不用。元組元素就像字符串元素,不可變。但是,如果元組中包含可變?cè)?,里面的元素是可變?如:列表)

特殊的情況,創(chuàng)建零個(gè)或一個(gè)元素的元組。零個(gè)元素直接用(),而一個(gè)元素的時(shí)候,需要額外加一個(gè)逗號(hào),丑但有效1,(1,)

    >>> empty = ()
    >>> singleton = 'hello',    # <-- note trailing comma
    >>> len(empty)
    0
    >>> len(singleton)
    1
    >>> singleton
    ('hello',)

集合

集合(set)是一個(gè)無序不重復(fù)元素的集,基本功能包括關(guān)系測(cè)試和消除重復(fù)元素。集合對(duì)象還支持 union(聯(lián)合),intersection(交),difference(差)和 sysmmetric difference(對(duì)稱差集)等數(shù)學(xué)運(yùn)算

大括號(hào){...}set()函數(shù)可以用來創(chuàng)建集合。注意:想要?jiǎng)?chuàng)建空集合,你必須使用set()而不是{}。后者用于創(chuàng)建空字典

    >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
    >>> print(basket)      # show that duplicates have been removed
    {'orange', 'banana', 'pear', 'apple'}
    >>> 'orange' in basket # fast membership testing
    True
    >>> 'crabgrass' in basket
    False

    >>> # Demonstrate set operations on unique letters from two words
    ...
    >>> a = set('abracadabra')
    >>> b = set('alacazam')
    >>> a          # unique letters in a
    {'a', 'r', 'b', 'c', 'd'}
    >>> a - b      # letters in a but not in b
    {'r', 'd', 'b'}
    >>> a | b      # letters in either a or b
    {'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
    >>> a & b      # letters in both a and b
    {'a', 'c'}
    >>> a ^ b      # letters in a or b but not both
    {'r', 'd', 'b', 'm', 'z', 'l'}

以上例子證明,集合支持自動(dòng)的去重(但亂序)和數(shù)學(xué)運(yùn)算

集合也支持推導(dǎo)式(并去重)

    >>> a = {x for x in 'abracadabra' if x not in 'abc'}
    >>> a
    {'r', 'd'}

字典

字典在其他語言中,可能被稱為“關(guān)聯(lián)數(shù)組”(associative arrays)。字典以關(guān)鍵字為索引,關(guān)鍵字可以是任意不可變類型,通常用字符串或數(shù)值。如果元組中只包含字符串和數(shù)字,它可以做為關(guān)鍵字,如果它直接或間接的包含了可變對(duì)象,就不能當(dāng)做關(guān)鍵字。不能用列表做關(guān)鍵字,因?yàn)榱斜砜梢杂盟饕?、切割或?code>append()和extend()等方法改變

字典的主要操作是依據(jù)鍵來存儲(chǔ)和析取值。也可以用del來刪除鍵:值對(duì)(key:value)。如果你用一個(gè)已經(jīng)存在的關(guān)鍵字存儲(chǔ)值,以前為該關(guān)鍵字分配的值就會(huì)被遺忘。試圖從一個(gè)不存在的鍵中取值會(huì)導(dǎo)致錯(cuò)誤

    >>> tel = {'jack': 4098, 'sape': 4139}
    >>> tel['guido'] = 4127
    >>> tel
    {'sape': 4139, 'guido': 4127, 'jack': 4098}
    >>> tel['jack']
    4098
    >>> del tel['sape']
    >>> tel['irv'] = 4127
    >>> tel
    {'guido': 4127, 'irv': 4127, 'jack': 4098}
    >>> list(tel.keys())
    ['irv', 'guido', 'jack']
    >>> sorted(tel.keys())
    ['guido', 'irv', 'jack']
    >>> 'guido' in tel
    True
    >>> 'jack' not in tel
    False

dict()構(gòu)造函數(shù)可以直接從key-value對(duì)中創(chuàng)建字典。此外,字典推導(dǎo)式可以從任意的鍵值表達(dá)式中創(chuàng)建字典

    >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
    {'sape': 4139, 'jack': 4098, 'guido': 4127}
    >>> dict(sape=4139, guido=4127, jack=4098)
    {'sape': 4139, 'jack': 4098, 'guido': 4127}
    >>> {x: x**2 for x in (2, 4, 6)}
    {2: 4, 4: 16, 6: 36}

循環(huán)技巧

循環(huán)字典,使用item()方法:

    >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
    >>> for k, v in knights.items():
    ...     print(k, v)
    ...
    gallahad the pure
    robin the brave

循環(huán)序列,使用enumerate()

    >>> for i, v in enumerate(['tic', 'tac', 'toe']):
    ...     print(i, v)
    ...
    0 tic
    1 tac
    2 toe

同時(shí)循環(huán)兩個(gè)或更多序列,使用zip()整體打包:

    >>> questions = ['name', 'quest', 'favorite color']
    >>> answers = ['lancelot', 'the holy grail', 'blue']
    >>> for q, a in zip(questions, answers):
    ...     print('What is your {0}?  It is {1}.'.format(q, a))
    ...
    """ result:
    What is your name?  It is lancelot.
    What is your quest?  It is the holy grail.
    What is your favorite color?  It is blue.
    """

逆向循環(huán),使用reversed()

    >>> for i in reversed(range(1, 10, 2)):
    ...     print(i)
    ...
    9
    7
    5
    3
    1

排序去重后輸出,使用sorted()

    >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
    >>> for f in sorted(set(basket)):
    ...     print(f)
    ...
    apple
    banana
    orange
    pear

若要在循環(huán)內(nèi)部修改正在遍歷的序列(例如復(fù)制某些元素),建議您首先制作副本(!)

    >>> words = ['cat', 'window', 'defenestrate']
    >>> for w in words[:]:  # Loop over a slice copy of the entire list.
    ...     if len(w) > 6:
    ...         words.insert(0, w)
    ...
    >>> words
    ['defenestrate', 'cat', 'window', 'defenestrate']

條件判斷

  1. 比較操作符innot in審核值是否在一個(gè)區(qū)間之內(nèi),1 not in [2, 3]
  2. 比較操作符isis not比較兩個(gè)對(duì)象是否相同,[] is ()
  3. 比較操作可以傳遞,1 <= 3 > 2,比較操作符具有相同的優(yōu)先級(jí)
  4. 短路運(yùn)算符andor,參數(shù)從左向右解析,一旦結(jié)果可以確定即停止,短路操作符的返回值通常是最后一個(gè)變量0 and 1
  5. notandor同屬于邏輯操作符,優(yōu)先級(jí)not>and>or
  6. 總優(yōu)先級(jí):數(shù)值操作 > 比較操作 > 邏輯操作符
  7. C不同,Python在表達(dá)式內(nèi)不能賦值

序列對(duì)象可以與相同類型的其他對(duì)象作比較,比較操作按字典序進(jìn)行:首先比較前兩個(gè)元素,如果不同,就決定了比較的結(jié)果;如果相同,就比較后兩個(gè)元素,依此類推,直到所有序列都完成比較。如果兩個(gè)元素本身就是同樣類 型的序列,就遞歸字典序比較。如果兩個(gè)序列的所有子項(xiàng)都相等,就認(rèn)為序列相等。如果一個(gè)序列是另一個(gè)序列的初始子序列,較短的一個(gè)序列就小于另一個(gè)。字符串的字典序按照單字符的ASCII順序

模塊

模塊是包括Python定義和聲明的文件。文件名就是模塊名加上.py后綴。模塊的模塊名(做為一個(gè)字符串)可以由全局變量__name__得到

    # 先在桌面新建一個(gè)test.py,定義一個(gè)hello()方法
    >>> os.getcwd()
    '/home/my/Desktop'
    >>> import test
    >>> test.hello()
    hello world
    >>> test.__name__
    'test'

除了包含函數(shù)定義外,模塊也可以包含可執(zhí)行語句。這些語句一般用來初始化模塊。他們僅在 第一次 被導(dǎo)入的地方執(zhí)行一次

    # 在test.py中加入一個(gè)輸出語句
    >>> import test
    import mod test successfully!

每個(gè)模塊都有自己私有的符號(hào)表,被模塊內(nèi)所有的函數(shù)定義作為全局符號(hào)表使用。因此,模塊的作者可以在模塊內(nèi)部使用全局變量,而無需擔(dān)心它與某個(gè)用戶的全局變量意外沖突

模塊可以導(dǎo)入其他的模塊。一個(gè)(好的)習(xí)慣是將所有的import語句放在模塊的開始(或者是腳本),這并非強(qiáng)制。被導(dǎo)入的模塊名會(huì)放入當(dāng)前模塊的全局符號(hào)表中

import語句的一個(gè)變體直接從被導(dǎo)入的模塊中導(dǎo)入命名到本模塊的語義表中

    >>> from fibo import fib, fib2
    >>> fib(500)
    1 1 2 3 5 8 13 21 34 55 89 144 233 377

作為腳本來執(zhí)行模塊

只需要添加一個(gè)if判定:

    if __name__ == "__main__":
        import sys
        fib(int(sys.argv[1]))

然后用python fibo.py 50即可傳入?yún)?shù)并執(zhí)行

模塊的搜索路徑

導(dǎo)入一個(gè)叫spam的模塊時(shí),解釋器先在當(dāng)前目錄中搜索名為spam.py的文件。如果沒有找到的話,接著會(huì)到sys.path變量中給出的目錄列表中查找,變量的初始值如下:

  • 輸入腳本的目錄(當(dāng)前目錄)
  • 環(huán)境變量PYTHONPATH表示的目錄列表中搜索
  • Python默認(rèn)安裝路徑中的搜索
    >>> sys.path
    ['', '/usr/lib/python35.zip', '/usr/lib/python3.5', '/usr/lib/python3.5/plat-x86_64-linux-gnu', '/usr/lib/python3.5/lib-dynload', '/usr/local/lib/python3.5/dist-packages', '/usr/lib/python3/dist-packages']

需要注意的是由于這些目錄中包含有搜索路徑中運(yùn)行的腳本,所以這些腳本不應(yīng)該和標(biāo)準(zhǔn)模塊重名,否則在導(dǎo)入模塊時(shí)Python會(huì)嘗試把這些腳本當(dāng)作模塊來加載。這通常會(huì)引發(fā)錯(cuò)誤

dir()函數(shù)

內(nèi)置函數(shù)dir()用于按模塊名搜索模塊定義,它返回一個(gè)字符串類型的存儲(chǔ)列表

    >>> import fibo, sys
    >>> dir(fibo)
    ['__name__', 'fib', 'fib2']

包通常是使用用“圓點(diǎn)模塊名”的結(jié)構(gòu)化模塊命名空間。例如,名為 A.B 的模塊表示了名為A的包中名為B的子模塊。可以避免全局變量之間的相互沖突

包的結(jié)構(gòu)可能是這樣的

    sound/              Top-level package
          __init__.py   Initialize the sound package
          formats/      Subpackage for file format conversions
                  __init__.py
                  wavread.py
                  wavwrite.py
                  aiffread.py
                  aiffwrite.py
                  auread.py
                  auwrite.py
                  ...
          effects/      Subpackage for sound effects
                  __init__.py
                  echo.py
                  surround.py
                  reverse.py
                  ...
          filters/      Subpackage for filters
                  __init__.py
                  equalizer.py
                  vocoder.py
                  karaoke.py
                  ...

當(dāng)導(dǎo)入這個(gè)包時(shí),Python通過sys.path搜索路徑查找包含這個(gè)包的子目錄

為了讓Python將目錄當(dāng)做內(nèi)容包,目錄中必須包含__init__.py文件。這是為了避免一個(gè)含有爛俗名字的目錄無意中隱藏了稍后在模塊搜索路徑中出現(xiàn)的有效模塊,比如string。最簡(jiǎn)單的情況下,只需要一個(gè)空的__init__.py文件即可。當(dāng)然它也可以執(zhí)行包的初始化代碼,或者定義稍后介紹的__all__變量

導(dǎo)入模塊的方法:

    # 1
    import sound.effects.echo
    sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)
    # 2
    from sound.effects import echo
    echo.echofilter(input, output, delay=0.7, atten=4)
    # 3
    from sound.effects.echo import echofilter
    echofilter(input, output, delay=0.7, atten=4)

from sound.effects import *可能會(huì)很慢,或者出現(xiàn)一些意外(強(qiáng)烈不推薦的寫法)。但是這種情況難以避免,對(duì)于包的作者來說唯一的解決方案就是提供一個(gè)明確的包索引:執(zhí)行from package import *時(shí),如果包中的__init__.py代碼定義了一個(gè)名為__all__的列表,就會(huì)按照列表中給出的模塊名進(jìn)行導(dǎo)入(可以避免導(dǎo)入所有模塊)

輸入輸出

print()函數(shù)對(duì)于每個(gè)參數(shù),自動(dòng)用空格分開輸出,以下是比較優(yōu)雅的輸出方式(不是拼接的):

    >>> print('We are the {} who say "{}!"'.format('knights', 'Ni'))
    We are the knights who say "Ni!"

    >>> print('{0} and {1}'.format('spam', 'eggs'))
    spam and eggs
    >>> print('{1} and {0}'.format('spam', 'eggs'))
    eggs and spam

    >>> print('This {food} is {adjective}.'.format(
    ...       food='spam', adjective='absolutely horrible'))
    This spam is absolutely horrible.

    >>> print('The story of {0}, {1}, and {other}.'.format('Bill', 'Manfred',
                                                           other='Georg'))
    The story of Bill, Manfred, and Georg.

    >>> import math
    >>> print('The value of PI is approximately {0:.3f}.'.format(math.pi))
    The value of PI is approximately 3.142.

可以看到,{}用于指代格式化的內(nèi)容,里面可以規(guī)定順序、關(guān)鍵字等

字段名后允許可選的':'和格式指令。這允許對(duì)值的格式化加以更深入的控制

更多例子

舊式的字符串格式化,使用%

    >>> print("Name:%10s Age:%8d Height:%8.2f" % ("Aviad", 25, 1.83))
    Name:     Aviad Age:      25 Height:    1.83

更多例子

文件讀寫

函數(shù)open()返回文件對(duì)象,這個(gè)函數(shù)通常需要兩個(gè)字符串參數(shù):文件名、打開模式

這里要特別注意的是,對(duì)于非文本文件,要在模式后面加上'b',否則會(huì)錯(cuò)誤當(dāng)做文本文件,修改一些平臺(tái)有關(guān)的行結(jié)束符字符(Python打開文本,會(huì)默認(rèn)會(huì)轉(zhuǎn)換成平臺(tái)相關(guān)的行結(jié)束符)

    >>> f = open('openfile', 'w');

文件的對(duì)象方法:

f.read(size)方法用于讀取文件內(nèi)容,size是可選的數(shù)值,如果沒有指定size或者指定為負(fù)數(shù),就會(huì)讀取并返回整個(gè)文件。當(dāng)文件大小為當(dāng)前機(jī)器內(nèi)存兩倍時(shí),就會(huì)產(chǎn)生問題。反之,會(huì)盡可能按比較大的size讀取和返回?cái)?shù)據(jù)。如果到了文件末尾,f.read()會(huì)返回一個(gè)空字符串''

    >>> f.read()
    'This is the entire file.\n'
    >>> f.read()
    ''
    >>> f.read()
    ''

f.readline()從文件中讀取單獨(dú)一行,字符串結(jié)尾會(huì)自動(dòng)加上一個(gè)換行符\n,只有當(dāng)文件最后一行沒有以換行符結(jié)尾時(shí),這一操作才會(huì)被忽略。這樣返回值就不會(huì)有混淆,如果f.readline()返回一個(gè)空字符串,那就表示到達(dá)了文件末尾,如果是一個(gè)空行,就會(huì)描述為'\n',一個(gè)只包含換行符的字符串

    >>> f.readline()
    'This is the first line of the file.\n'
    >>> f.readline()
    'Second line of the file\n'
    >>> f.readline()
    ''

高效的讀取方法

    >>> for line in f:
    ...     print(line, end='')
    ...

    ''' result:
    This is the first line of the file.
    Second line of the file
    '''

將所有行讀到一個(gè)列表中

    >>> f.seek(0)
    >>> list(f)
    ['dfsafdsa\n', 'dfsafds\n', 'fdsagdfhfdh\n', 'hfgng\n', 'nh\n', 'trh\n', 'trh\n', 'tr\n', '\n', '\n', 'h\n', 'tr\n', 'htrhtr\n', 'h\n', 'tr\n', 'htr\n', 'h\n', 'tr\n', '\n', '\n', '\n', 'htrhtr\n']
    >>> list(f)
    []
    >>> f.seek(0)
    >>> f.readlines()
    ['dfsafdsa\n', 'dfsafds\n', 'fdsagdfhfdh\n', 'hfgng\n', 'nh\n', 'trh\n', 'trh\n', 'tr\n', '\n', '\n', 'h\n', 'tr\n', 'htrhtr\n', 'h\n', 'tr\n', 'htr\n', 'h\n', 'tr\n', '\n', '\n', '\n', 'htrhtr\n']

f.write(string)方法將string的內(nèi)容寫入文件(是覆蓋還是追加,視打開文件的模式而定),Python3.5.2經(jīng)測(cè)試沒有返回值。如果想寫入非字符串的內(nèi)容,首先要轉(zhuǎn)換為字符串

    >>> f.write('This is a test\n')

    >>> value = ('the answer', 42)
    >>> s = str(value)
    >>> f.write(s)

文件對(duì)象的指針

f.tell()返回一個(gè)整數(shù),代表文件對(duì)象在文件中的指針位置,該數(shù)值計(jì)量了自文件開頭到指針處的比特?cái)?shù)。需要改變文件對(duì)象指針話話,使用f.seek(offset,from_what)。指針在該操作中從指定的引用位置移動(dòng)offset比特,引用位置由from_what參數(shù)指定。from_what值為0表示自文件起始處開始,1表示自當(dāng)前文件指針位置開始,2表示自文件末尾開始(配合的offset是負(fù)數(shù))。from_what可以忽略,其默認(rèn)值為0

    >>> f = open('workfile', 'rb+')
    >>> f.write(b'0123456789abcdef')
    16
    >>> f.seek(5)     # Go to the 6th byte in the file
    5
    >>> f.read(1)
    b'5'
    >>> f.seek(-3, 2) # Go to the 3rd byte before the end
    13
    >>> f.read(1)
    b'd'

當(dāng)你使用完一個(gè)文件時(shí),調(diào)用f.close()方法就可以關(guān)閉它并釋放其占用的所有系統(tǒng)資源。在調(diào)用f.close()方法后,試圖再次使用文件對(duì)象將會(huì)自動(dòng)失敗

    >>> f.close()
    >>> f.read()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: I/O operation on closed file
    >>> f
    <closed file 'test.txt', mode 'a+' at 0x7f8cd23ca5d0>

使用with處理文件對(duì)象的好習(xí)慣

使用關(guān)鍵字with的先進(jìn)之處在于文件用完后會(huì)自動(dòng)關(guān)閉,就算發(fā)生異常也沒關(guān)系。它是try-finally塊的簡(jiǎn)寫

    >>> with open('test.txt', 'r') as f:
    ...     read_data = f.read()
    ...
    >>> read_data
    'dfsafdsa\n'
    >>> f.closed
    True

以上的語句相當(dāng)于,先調(diào)用了open()函數(shù),并將返回值賦給f,然后里面的語句是try塊里面的內(nèi)容,finally的內(nèi)容隱藏在f對(duì)象的__exit__方法中,即f.close()。這里需要注意的是,這里出現(xiàn)異常的話,仍然需要自己定義try-except來處理。參考鏈接

使用json存儲(chǔ)格式化數(shù)據(jù)

需要使用json標(biāo)準(zhǔn)模塊,import json

    >>> json.dumps(['foo', {'bar': ('baz', None, 1.0, 2)}])
    '["foo", {"bar": ["baz", null, 1.0, 2]}]'
    >>> json.loads('["foo", {"bar":["baz", null, 1.0, 2]}]')
    ['foo', {'bar': ['baz', None, 1.0, 2]}]

    >>> x
    '["foo", {"bar": ["baz", null, 1.0, 2]}]'
    >>> f = open("test.txt", "w+")
    >>> f.read()
    ''
    >>> f.tell()
    0
    >>> json.dump(x, f)
    >>> f.seek(0)
    >>> f.read()
    '"[\\"foo\\", {\\"bar\\": [\\"baz\\", null, 1.0, 2]}]"'
    >>> f.seek(0)
    >>> y = json.load(f)
    >>> y
    u'["foo", {"bar": ["baz", null, 1.0, 2]}]'
    >>> z = json.loads(y)
    >>> z[1]["bar"][2]
    1.0

錯(cuò)誤和異常

語法錯(cuò)誤

語法錯(cuò)誤,也被稱作解析錯(cuò)誤(最常見)

    >>> while True print('Hello world')
      File "<stdin>", line 1
        while True print('Hello world')
                       ^
    SyntaxError: invalid syntax

異常

即使一條語句或表達(dá)式在語法上是正確的,當(dāng)試圖執(zhí)行它時(shí)也可能會(huì)引發(fā)錯(cuò)誤。運(yùn)行期檢測(cè)到的錯(cuò)誤稱為異常,并且程序不會(huì)無條件的崩潰:很快,你將學(xué)到如何在Python程序中處理它們。然而,大多數(shù)異常都不會(huì)被程序處理,像這里展示的一樣最終會(huì)產(chǎn)生一個(gè)錯(cuò)誤信息

    >>> 10 * (1/0)
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    ZeroDivisionError: int division or modulo by zero
    >>> 4 + spam*3
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    NameError: name 'spam' is not defined
    >>> '2' + 2
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    TypeError: Can't convert 'int' object to str implicitly

錯(cuò)誤信息的最后一行指出發(fā)生了什么錯(cuò)誤。異常也有不同的類型,異常類型做為錯(cuò)誤信息的一部分顯示出來

異常處理

try語句按如下方式工作:

  1. 首先,執(zhí)行try子句 (在tryexcept關(guān)鍵字之間的部分)
  2. 如果沒有異常發(fā)生,except子句在try語句執(zhí)行完畢后就被忽略了
  3. 如果在try子句執(zhí)行過程中發(fā)生了異常,那么該子句其余的部分就會(huì)被忽略。如果異常匹配于except關(guān)鍵字后面指定的異常類型,就執(zhí)行對(duì)應(yīng)的except子句。然后繼續(xù)執(zhí)行try語句之后的代碼
  4. 如果發(fā)生了一個(gè)異常,在except子句中沒有與之匹配的分支,它就會(huì)傳遞到上一級(jí)try語句中。如果最終仍找不到對(duì)應(yīng)的處理語句,它就成為一個(gè)未處理異常,終止程序運(yùn)行,顯示提示信息

一個(gè)except子句可以在括號(hào)中列出多個(gè)異常的名字去匹配相應(yīng)的異常;如果省略括號(hào),則表示全部匹配

    import sys

    try:
        f = open('myfile.txt')
        s = f.readline()
        i = int(s.strip())
    except OSError as err:
        print("OS error: {0}".format(err))
    except ValueError:
        print("Could not convert data to an integer.")
    except:
        print("Unexpected error:", sys.exc_info()[0])
        raise

try-except語句可以帶有一個(gè)else子句,該子句只能出現(xiàn)在所有except子句之后。當(dāng)try語句沒有拋出異常時(shí),需要執(zhí)行一些代碼,可以使用這個(gè)子句

    for arg in sys.argv[1:]:
        try:
            f = open(arg, 'r')
        except IOError:
            print('cannot open', arg)
        else:
            print(arg, 'has', len(f.readlines()), 'lines')
            f.close()

使用else子句比在try子句中附加代碼要好,因?yàn)檫@樣可以避免try-except意外的截獲本來不屬于它們保護(hù)的那些代碼拋出的異常

所以,except里面的代碼是錯(cuò)誤的時(shí)候執(zhí)行的;else里面的代碼是正確的時(shí)候執(zhí)行的;finally里面的代碼是無論如何都會(huì)執(zhí)行的(后面講到)

發(fā)生異常時(shí),可能會(huì)有一個(gè)附屬值,作為異常的參數(shù)存在。這個(gè)參數(shù)是否存在、是什么類型,依賴于異常的類型。通常可以為except子句指定(as)一個(gè)變量,可以直接print出來看

異常處理器不僅僅處理那些在 try 子句中立刻發(fā)生的異常,也會(huì)處理那些 try 子句中調(diào)用的函數(shù)內(nèi)部發(fā)生的異常

    >>> def this_fails():
    ...     x = 1/0
    ...
    >>> try:
    ...     this_fails()
    ... except ZeroDivisionError as err:
    ...     print('Handling run-time error:', err)
    ...
    Handling run-time error: int division or modulo by zero

raise語句允許程序員強(qiáng)制拋出一個(gè)指定的異常(必須是一個(gè)異常實(shí)例或異常類)

如果你需要明確一個(gè)異常是否拋出,但不想處理它,raise語句可以讓你很簡(jiǎn)單的重新拋出該異常(一些監(jiān)控工具的實(shí)現(xiàn))

    >>> try:
    ...     raise NameError('HiThere')
    ... except NameError:
    ...     print('An exception flew by!')
    ...     raise
    ...
    ''' result:
    An exception flew by!
    Traceback (most recent call last):
      File "<stdin>", line 2, in ?
    NameError: HiThere
    '''

用戶可以自定義異常,異常類通常應(yīng)該直接或間接從Exception類派生,下面是一個(gè)例子,更多例子看這里

    >>> class MyError(Exception):
    ...     def __init__(self, value):
    ...         self.value = value
    ...     def __str__(self):
    ...         return repr(self.value)
    ...
    >>> try:
    ...     raise MyError(2*2)
    ... except MyError as e:
    ...     print('My exception occurred, value:', e.value)
    ...
    ''' result:
    My exception occurred, value: 4
    >>> raise MyError('oops!')
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
    __main__.MyError: 'oops!'
    '''

前面說到的finally語句,是無論什么情況都會(huì)執(zhí)行的功能,通常放的是清理行為,如果遇到異常,執(zhí)行完這段清理語句之后就結(jié)束了,下面是一個(gè)混合try-except-else-finally的代碼塊

    >>> def divide(x, y):
    ...     try:
    ...         result = x / y
    ...     except ZeroDivisionError:
    ...         print("division by zero!")
    ...     else:
    ...         print("result is", result)
    ...     finally:
    ...         print("executing finally clause")
    ...
    >>> divide(2, 1)
    result is 2
    executing finally clause
    >>> divide(2, 0)
    division by zero!
    executing finally clause
    >>> divide("2", "1")
    executing finally clause
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
      File "<stdin>", line 3, in divide
    TypeError: unsupported operand type(s) for /: 'str' and 'str'

在真實(shí)場(chǎng)景的應(yīng)用程序中,finally子句用于釋放外部資源(文件 或網(wǎng)絡(luò)連接之類的),無論它們的使用過程中是否出錯(cuò)

有些對(duì)象定義了標(biāo)準(zhǔn)的清理行為,無論對(duì)象操作是否成功,不再需要該對(duì)象的時(shí)候就會(huì)起作用。下面有兩段代碼:

    # para 1
    for line in open("myfile.txt"):
        print(line)

    # para 2
    with open("myfile.txt") as f:
        for line in f:
            print(line)

區(qū)別在于,第一段代碼沒有關(guān)閉打開的文件,而with語句可以使文件之類的對(duì)象確??偰芗皶r(shí)準(zhǔn)確地進(jìn)行清理。在第二段語句執(zhí)行后,文件對(duì)象總會(huì)被關(guān)閉,即使是在處理文件中的數(shù)據(jù)時(shí)出錯(cuò)也一樣可以

關(guān)于作用域的示例(注意變量的查找順序),以下例子:

    def scope_test():
        def do_local():
            spam = "local spam"
        def do_nonlocal():
            nonlocal spam
            spam = "nonlocal spam"
        def do_global():
            global spam
            spam = "global spam"
        spam = "test spam"
        do_local()
        print("After local assignment:", spam)
        do_nonlocal()
        print("After nonlocal assignment:", spam)
        do_global()
        print("After global assignment:", spam)

    scope_test()
    print("In global scope:", spam)

類定義語法

類定義就像函數(shù)定義,要先執(zhí)行才能生效

    class ClassName:
        <statement-1>
        .
        .
        .
        <statement-N>

Python的類對(duì)象支持屬性引用和實(shí)例化,如:

    class MyClass:
        """A simple example class"""
        i = 12345
        def f(self):
            return 'hello world'

屬性引用: MyClass.i, MyClass.f, MyClass.__doc__
實(shí)例化: x = MyClass()

構(gòu)造函數(shù)的定義:

    def __init__(self[, other params]):
        pass

類方法的第一個(gè)參數(shù)一定是self,在調(diào)用的時(shí)候省略傳入,代指實(shí)例本身

Python的實(shí)例對(duì)象唯一可用的操作是屬性引用。和局部變量一樣,數(shù)據(jù)屬性不需要聲明,第一次使用時(shí)它們就會(huì)生成

在調(diào)用方法的時(shí)候,如:x.f()事實(shí)上是調(diào)用了MyClass.f(x),這就是要在定義類方法的時(shí)候加上參數(shù)self的原因

類和實(shí)例變量

一般來說,實(shí)例變量用于對(duì)每一個(gè)實(shí)例都是唯一的數(shù)據(jù),類變量用于類的所有實(shí)例共享的屬性和方法

    class Dog:

        kind = 'canine'         # class variable shared by all instances

        def __init__(self, name):
            self.name = name    # instance variable unique to each instance

    >>> d = Dog('Fido')
    >>> e = Dog('Buddy')
    >>> d.kind                  # shared by all dogs
    'canine'
    >>> e.kind                  # shared by all dogs
    'canine'
    >>> d.name                  # unique to d
    'Fido'
    >>> e.name                  # unique to e
    'Buddy'

這樣會(huì)出現(xiàn),可變對(duì)象作共享數(shù)據(jù)的問題

    class Dog:

        tricks = []             # mistaken use of a class variable

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

        def add_trick(self, trick):
            self.tricks.append(trick)

    >>> d = Dog('Fido')
    >>> e = Dog('Buddy')
    >>> d.add_trick('roll over')
    >>> e.add_trick('play dead')
    >>> d.tricks                # unexpectedly shared by all dogs
    ['roll over', 'play dead']

正確的做法是,將這個(gè)列表作為一個(gè)實(shí)例變量,即放入構(gòu)造方法中賦值

    class Dog:

        def __init__(self, name):
            self.name = name
            self.tricks = []    # creates a new empty list for each dog

        def add_trick(self, trick):
            self.tricks.append(trick)

    >>> d = Dog('Fido')
    >>> e = Dog('Buddy')
    >>> d.add_trick('roll over')
    >>> e.add_trick('play dead')
    >>> d.tricks
    ['roll over']
    >>> e.tricks
    ['play dead']

類的繼承

派生類的定義例子:

    class DerivedClassName(BaseClassName):
        <statement-1>
        .
        .
        .
        <statement-N>

派生類定義的執(zhí)行過程和基類是一樣的。構(gòu)造派生類對(duì)象時(shí),就記住了基類。這在解析屬性引用的時(shí)候尤其有用:如果在類中找不到請(qǐng)求調(diào)用的屬性,就搜索基類

如果基類是由別的類派生而來,這個(gè)規(guī)則會(huì)遞歸的應(yīng)用上去。方法引用按如下規(guī)則解析:搜索對(duì)應(yīng)的類屬性,必要時(shí)沿基類鏈逐級(jí)搜索,如果找到了函數(shù)對(duì)象這個(gè)方法引用就是合法的

派生類可能會(huì)覆蓋其基類的方法。因?yàn)榉椒ㄕ{(diào)用同一個(gè)對(duì)象中的其它方法時(shí)沒有特權(quán),基類的方法調(diào)用同一個(gè)基類的方法時(shí),可能實(shí)際上最終調(diào)用了派生類中的覆蓋方法

派生類中調(diào)用基類方法,這時(shí)需要傳入self參數(shù)了,BaseClassName.methodname(self, arguments)

判斷類或者實(shí)例之間的關(guān)系

  • 函數(shù)isinstance()用于檢查實(shí)例類型:isinstance(obj, int)只有在obj.__class__int或其它從int繼承的類型
  • 函數(shù)issubclass()用于檢查類繼承:issubclass(bool, int)True,因?yàn)?code>bool是int的子類
  • 然而,issubclass(float, int)False,因?yàn)?code>float不是int的子類

Python同樣有限的支持多繼承形式,例子如下:

    class DerivedClassName(Base1, Base2, Base3):
        <statement-1>
        .
        .
        .
        <statement-N>

搜索順序:如果在DerivedClassName(示例中的派生類)中沒有找到某個(gè)屬性,就會(huì)搜索Base1,然后(遞歸的)搜索其基類,如果最終沒有找到,就搜索Base2,以此類推

上面的搜索順序有一個(gè)問題,如果多個(gè)Base擁有相同的基類,就會(huì)發(fā)生重復(fù)訪問基類的情況,這時(shí),可以用super()來動(dòng)態(tài)改變解析順序

為了防止重復(fù)訪問基類,通過動(dòng)態(tài)的線性化算法,每個(gè)類都按從左到右的順序特別指定了順序,每個(gè)祖先類只調(diào)用一次

私有變量

Python中不存在只能從對(duì)象內(nèi)部訪問的實(shí)例變量,一般規(guī)定私有變量以一個(gè)下劃線開頭命名,例如_spam。此外,形如__spam,以兩個(gè)下劃線開頭,后面至多只有一個(gè)下劃線的變量,會(huì)被替換為_classname__spam

    >>> class Ms1:
    ...     def _update(self):
    ...             print(345)
    ...     def __hello(self):
    ...             print("hello world")
    ...
    >>> m = Ms1()
    >>> m._Ms1__hello()
    hello world
    >>> m.__hello()
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'Ms1' object has no attribute '__hello'
    >>> m._Ms1__hello()
    hello world
    >>> m._update()
    345

迭代器

大多數(shù)容器對(duì)象都可以用for遍歷,如for elem in [1, 2, 3]。在后臺(tái),for語句在容器對(duì)象中調(diào)用iter(),該函數(shù)返回一個(gè)定義了__next__()方法的迭代器對(duì)象,它在容器中逐一訪問元素,當(dāng)沒有后續(xù)的元素時(shí),__next__()拋出一個(gè)StopIteration異常來通知for語句結(jié)束循環(huán)

可以使用內(nèi)建的next()函數(shù)來調(diào)用__next__()方法

    >>> s = 'abc'
    >>> it = iter(s)
    >>> it
    <iterator object at 0x00A1DB50>
    >>> next(it)
    'a'
    >>> next(it)
    'b'
    >>> next(it)
    'c'
    >>> next(it)
    Traceback (most recent call last):
      File "<stdin>", line 1, in ?
        next(it)
    StopIteration

根據(jù)之前所說的原理,可以很容易的為自己的類添加迭代器行為,以下例子:

    class Reverse:
        """Iterator for looping over a sequence backwards."""
        def __init__(self, data):
            self.data = data
            self.index = len(data)
        def __iter__(self):
            return self
        def __next__(self):
            if self.index == 0:
                raise StopIteration
            self.index = self.index - 1
            return self.data[self.index]

    >>> rev = Reverse('spam')
    >>> iter(rev)
    <__main__.Reverse object at 0x00A1DB50>
    >>> for char in rev:
    ...     print(char)
    ...
    m
    a
    p
    s

生成器

生成器是創(chuàng)建迭代器的簡(jiǎn)單而強(qiáng)大的工具,它們寫起來就像是正規(guī)的函數(shù),需要返回?cái)?shù)據(jù)的時(shí)候使用yield語句

    def reverse(data):
        for index in range(len(data)-1, -1, -1):
            yield data[index]

    >>> for char in reverse('golf'):
    ...     print(char)
    ...
    f
    l
    o
    g

前面中描述了基于類的迭代器,它能做的每一件事生成器也能做到。因?yàn)樽詣?dòng)創(chuàng)建了__iter__()__next__()方法,生成器顯得如此簡(jiǎn)潔。除了創(chuàng)建和保存程序狀態(tài)的自動(dòng)方法,當(dāng)發(fā)生器終結(jié)時(shí),還會(huì)自動(dòng)拋出StopIteration異常。綜上所述,這些功能使得編寫一個(gè)正規(guī)函數(shù)成為創(chuàng)建迭代器的最簡(jiǎn)單方法

更多關(guān)于標(biāo)準(zhǔn)庫(kù)、包管理等的內(nèi)容見手冊(cè)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 定義類并創(chuàng)建實(shí)例 在Python中,類通過 class 關(guān)鍵字定義。以 Person 為例,定義一個(gè)Person類...
    績(jī)重KF閱讀 4,123評(píng)論 0 13
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,871評(píng)論 18 399
  • Python是一種對(duì)代碼風(fēng)格很重視的語言,從縮進(jìn)就能看出這一點(diǎn),Python強(qiáng)調(diào)易于理解。最近在負(fù)責(zé)代碼重構(gòu)的工作...
    知曰閱讀 11,369評(píng)論 1 85
  • 前言 ||| 第二章 使用ArcPy編寫腳本 Python支持大部分在其他語言中出現(xiàn)的編程結(jié)構(gòu)。在本章內(nèi)容中,我們...
    muyan閱讀 90,828評(píng)論 10 55
  • 那一年,2008。南方大雪災(zāi)。還在北方冰天雪地里搓手取暖的我,想象著南方的雪到底是多大。 那一年,2008。西...
    情風(fēng)古陽草閱讀 242評(píng)論 0 0

友情鏈接更多精彩內(nèi)容