Python的基礎進階

摘一顆星星發表於2020-10-06

Python的基礎進階

1、函式

  1. 函式的定義
  2. 函式的呼叫
  3. 函式文件
  4. 函式引數
  5. 函式的返回值
  6. 變數作用域

2、Lambda-表示式

  1. 匿名函式定義
  2. 匿名函式的應用

3、類與物件

  1. 屬性和方法組成物件
  2. self是什麼
  3. Python的魔法方法
  4. 共有和私用
  5. 繼承
  6. 組合
  7. 類、類物件和例項物件
  8. 什麼是繫結
  9. 一些相關的內建函式

4、魔法方法

  1. 基本的魔法方法
  2. 算數運算子
  3. 反算數運算子
  4. 增值賦值運算
  5. 一元運算子
  6. 屬性訪問
  7. 描述符
  8. 定製序列
  9. 迭代器

1、函式

1.函式的定義

  • 函式以def關鍵詞開頭,後接函式名和圓括號()。
  • 函式執行的程式碼以冒號起始,並且縮排。
  • return [表示式]結束函式,選擇性地返回一個值給呼叫方。不帶表示式的return相當於返回None。
def functionname (parameters):
       "函式_文件字串"
        function_suite
        return [expression]

2.函式的呼叫

【例子】

def printme(str):
    print(str)


printme("我要呼叫使用者自定義函式!")  # 我要呼叫使用者自定義函式!
printme("再次呼叫同一函式")  # 再次呼叫同一函式
temp = printme('hello') # hello
print(temp)  # None

3.函式文件

def MyFirstFunction(name):
    "函式定義過程中name是形參"
    # 因為Ta只是一個形式,表示佔據一個引數位置
    print('傳遞進來的{0}叫做實參,因為Ta是具體的引數值!'.format(name))


MyFirstFunction('老馬的程式人生')  
# 傳遞進來的老馬的程式人生叫做實參,因為Ta是具體的引數值!

print(MyFirstFunction.__doc__)  
# 函式定義過程中name是形參

help(MyFirstFunction)
# Help on function MyFirstFunction in module __main__:
# MyFirstFunction(name)
#    函式定義過程中name是形參

4.函式引數

  • 位置引數 (positional argument)
  • 預設引數 (default argument)
  • 可變引數 (variable argument)
  • 關鍵字引數 (keyword argument)
  • 命名關鍵字引數 (name keyword argument)
  • 引數組合

1.位置引數

def functionname(arg1):
       "函式_文件字串"
       function_suite
       return [expression]
  • arg1 - 位置引數 ,這些引數在呼叫函式 (call function) 時位置要固定

2.預設引數

def functionname(arg1, arg2=v):
       "函式_文件字串"
       function_suite
       return [expression]
  • arg2 = v - 預設引數 = 預設值,呼叫函式時,預設引數的值如果沒有傳入,則被認為是預設值。
  • 預設引數一定要放在位置引數後面,不然程式會報錯。

【例子】

def printinfo(name, age=8):
    print('Name:{0},Age:{1}'.format(name, age))


printinfo('小馬')  # Name:小馬,Age:8
printinfo('小馬', 10)  # Name:小馬,Age:10
  • Python 允許函式呼叫時引數的順序與宣告時不一致,因為 Python 直譯器能夠用引數名匹配引數值。

【例子】

def printinfo(name, age):
    print('Name:{0},Age:{1}'.format(name, age))


printinfo(age=8, name='小馬')  # Name:小馬,Age:8

3.可變引數

  • 可變引數就是傳入的引數個數是可變的,可以是 0, 1, 2 到任意個,是不定長的引數。
def functionname(arg1, arg2=v, *args):
       "函式_文件字串"
       function_suite
       return [expression]
  • *args - 可變引數,可以是從零個到任意個,自動組裝成元組。
  • 加了星號(*)的變數名會存放所有未命名的變數引數。
    【例子】
def printinfo(arg1, *args):
    print(arg1)
    for var in args:
        print(var)


printinfo(10)  # 10
printinfo(70, 60, 50)
# 70
# 60
# 50

4.關鍵字引數

def functionname(arg1, arg2=v, args, **kw):
       "函式_文件字串"
       function_suite
       return [expression]
  • **kw - 關鍵字引數,可以是從零個到任意個,自動組裝成字典。

【例子】

def printinfo(arg1, *args, **kwargs):
    print(arg1)
    print(args)
    print(kwargs)


printinfo(70, 60, 50)
# 70
# (60, 50)
# {}
printinfo(70, 60, 50, a=1, b=2)
# 70
# (60, 50)
# {'a': 1, 'b': 2}

「可變引數」和「關鍵字引數」的同異總結:

  • 可變引數允許傳入零個到任意個引數,它們在函式呼叫時自動組裝為一個元組 (tuple)。
  • 關鍵字引數允許傳入零個到任意個引數,它們在函式內部自動組裝為一個字典 (dict)。

5.命名關鍵字引數

def functionname(arg1, arg2=v, args, *, nkw, *kw):
       "函式_文件字串"
       function_suite
       return [expression]
  • *, nkw - 命名關鍵字引數,使用者想要輸入的關鍵字引數,定義方式是在nkw 前面加個分隔符 *。
  • 如果要限制關鍵字引數的名字,就可以用「命名關鍵字引數」
  • 使用命名關鍵字引數時,要特別注意不能缺少引數名。
    【例子】
def printinfo(arg1, *, nkw, **kwargs):
    print(arg1)
    print(nkw)
    print(kwargs)


printinfo(70, nkw=10, a=1, b=2)
# 70
# 10
# {'a': 1, 'b': 2}

printinfo(70, 10, a=1, b=2)
# TypeError: printinfo() takes 1 positional argument but 2 were given
  • 沒有寫引數名nwk,因此 10 被當成「位置引數」,而原函式只有 1 個位置函式,現在呼叫了 2 個,因此程式會報錯。

6.引數組合
在 Python 中定義函式,可以用位置引數、預設引數、可變引數、命名關鍵字引數和關鍵字引數,這 5 種引數中的 4 個都可以一起使用,但是注意,引數定義的順序必須是:

  • 位置引數、預設引數、可變引數和關鍵字引數。
  • 位置引數、預設引數、命名關鍵字引數和關鍵字引數。

要注意定義可變引數和關鍵字引數的語法:

  • *args 是可變引數,args 接收的是一個 tuple
  • **kw 是關鍵字引數,kw 接收的是一個 dict

命名關鍵字引數是為了限制呼叫者可以傳入的引數名,同時可以提供預設值。定義命名關鍵字引數不要忘了寫分隔符 *,否則定義的是位置引數。

5.函式的返回值

【例子】

def add(a, b):
    return a + b


print(add(1, 2))  # 3
print(add([1, 2, 3], [4, 5, 6]))  # [1, 2, 3, 4, 5, 6]

【例子】

def back():
    return [1, '小馬的程式人生', 3.14]


print(back())  # [1, '小馬的程式人生', 3.14]

6.變數作用域

  • Python 中,程式的變數並不是在哪個位置都可以訪問的,訪問許可權決定於這個變數是在哪裡賦值的。
  • 定義在函式內部的變數擁有區域性作用域,該變數稱為區域性變數。
  • 定義在函式外部的變數擁有全域性作用域,該變數稱為全域性變數。
  • 區域性變數只能在其被宣告的函式內部訪問,而全域性變數可以在整個程式範圍內訪問。

【例子】

def discounts(price, rate):
    final_price = price * rate
    return final_price


old_price = float(input('請輸入原價:'))  # 98
rate = float(input('請輸入折扣率:'))  # 0.9
new_price = discounts(old_price, rate)
print('打折後價格是:%.2f' % new_price)  # 88.20
  • 當內部作用域想修改外部作用域的變數時,就要用到global和nonlocal關鍵字了。
    【例子】
num = 1


def fun1():
    global num  # 需要使用 global 關鍵字宣告
    print(num)  # 1
    num = 123
    print(num)  # 123


fun1()
print(num)  # 123

內嵌函式

【例子】

def outer():
    print('outer函式在這被呼叫')

    def inner():
        print('inner函式在這被呼叫')

    inner()  # 該函式只能在outer函式內部被呼叫


outer()
# outer函式在這被呼叫
# inner函式在這被呼叫

閉包

  • 是函數語言程式設計的一個重要的語法結構,是一種特殊的內嵌函式。
  • 如果在一個內部函式裡對外層非全域性作用域的變數進行引用,那麼內部函式就被認為是閉包。
  • 通過閉包可以訪問外層非全域性作用域的變數,這個作用域稱為
    閉包作用域。

【例子】

def funX(x):
    def funY(y):
        return x * y

    return funY


i = funX(8)
print(type(i))  # <class 'function'>
print(i(5))  # 40

【例子】閉包的返回值通常是函式。

def make_counter(init):
    counter = [init]

    def inc(): counter[0] += 1

    def dec(): counter[0] -= 1

    def get(): return counter[0]

    def reset(): counter[0] = init

    return inc, dec, get, reset


inc, dec, get, reset = make_counter(0)
inc()
inc()
inc()
print(get())  # 3
dec()
print(get())  # 2
reset()
print(get())  # 0

【例子】 如果要修改閉包作用域中的變數則需要 nonlocal 關鍵字

def outer():
    num = 10

    def inner():
        nonlocal num  # nonlocal關鍵字宣告
        num = 100
        print(num)

    inner()
    print(num)


outer()

# 100
# 100

【例子】如果一個函式在內部呼叫自身本身,這個函式就是遞迴函式。

# 利用迴圈
n = 5
for k in range(1, 5):
    n = n * k
print(n)  # 120

# 利用遞迴
def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n - 1)


print(factorial(5)) # 120

【例子】斐波那契數列 f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1

# 利用迴圈
i = 0
j = 1
lst = list([i, j])
for k in range(2, 11):
    k = i + j
    lst.append(k)
    i = j
    j = k
print(lst)  
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

# 利用遞迴
def recur_fibo(n):
    if n <= 1:
        return n
    return recur_fibo(n - 1) + recur_fibo(n - 2)


lst = list()
for k in range(11):
    lst.append(recur_fibo(k))
print(lst)  
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

2、Lambda 表示式

1.匿名函式的定義

在 Python 裡有兩類函式:

  • 第一類:用 def 關鍵詞定義的正規函式
  • 第二類:用 lambda 關鍵詞定義的匿名函式

Python 使用 lambda 關鍵詞來建立匿名函式,而非def關鍵詞,它沒有函式名,其語法結構如下:

lambda argument_list: expression
  • lambda - 定義匿名函式的關鍵詞。
  • argument_list -函式引數,它們可以是位置引數、預設引數、關鍵字引數,和正規函式裡的引數型別一樣。
  • :- 冒號,在函式引數和表示式中間要加個冒號。
  • expression - 只是一個表示式,輸入函式引數,輸出一些值。

注意:

  • expression 中沒有 return 語句,因為 lambda 不需要它來返回,表示式本身結果就是返回值。
  • 匿名函式擁有自己的名稱空間,且不能訪問自己引數列表之外或全域性名稱空間裡的引數。

【例子】

def sqr(x):
    return x ** 2


print(sqr)
# <function sqr at 0x000000BABD3A4400>

y = [sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

lbd_sqr = lambda x: x ** 2
print(lbd_sqr)
# <function <lambda> at 0x000000BABB6AC1E0>

y = [lbd_sqr(x) for x in range(10)]
print(y)
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


sumary = lambda arg1, arg2: arg1 + arg2
print(sumary(10, 20))  # 30

func = lambda *args: sum(args)
print(func(1, 2, 3, 4, 5))  # 15

2.匿名函式的應用

  • 函數語言程式設計是指程式碼中每一塊都是不可變的,都由純函式的形式組成。這裡的純函式,是指函式本身相互獨立、互不影響,對於相同的輸入,總會有相同的輸出,沒有任何副作用。

【例子】非函數語言程式設計

def f(x):
    for i in range(0, len(x)):
        x[i] += 10
    return x


x = [1, 2, 3]
f(x)
print(x)
# [11, 12, 13]

匿名函式 常常應用於函數語言程式設計的高階函式 (high-order function)中,主要有兩種形式:

  • 引數是函式 (filter, map)
  • 返回值是函式 (closure)

如,在 filter和map函式中的應用:

  • filter(function, iterable) 過濾序列,過濾掉不符合條件的元素,返回一個迭代器物件,如果要轉換為列表,可以使用
    list() 來轉換。

【例子】

odd = lambda x: x % 2 == 1
templist = filter(odd, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(templist))  # [1, 3, 5, 7, 9]
  • map(function, *iterables) 根據提供的函式對指定序列做對映。
m1 = map(lambda x: x ** 2, [1, 2, 3, 4, 5])
print(list(m1))  
# [1, 4, 9, 16, 25]

m2 = map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
print(list(m2))  
# [3, 7, 11, 15, 19]

3、類與物件

1.物件 = 屬性 + 方法

物件是類的例項。換句話說,類主要定義物件的結構,然後我們以類為模板建立物件。類不但包含方法定義,而且還包含所有例項共享的資料。

  • 封裝:資訊隱蔽技術

我們可以使用關鍵字 class 定義 Python 類,關鍵字後面緊跟類的名稱、分號和類的實現。

【例子】

class Turtle:  # Python中的類名約定以大寫字母開頭
    """關於類的一個簡單例子"""
    # 屬性
    color = 'green'
    weight = 10
    legs = 4
    shell = True
    mouth = '大嘴'

    # 方法
    def climb(self):
        print('我正在很努力的向前爬...')

    def run(self):
        print('我正在飛快的向前跑...')

    def bite(self):
        print('咬死你咬死你!!')

    def eat(self):
        print('有得吃,真滿足...')

    def sleep(self):
        print('困了,睡了,晚安,zzz')


tt = Turtle()
print(tt)
# <__main__.Turtle object at 0x0000007C32D67F98>

print(type(tt))
# <class '__main__.Turtle'>

print(tt.__class__)
# <class '__main__.Turtle'>

print(tt.__class__.__name__)
# Turtle

tt.climb()
# 我正在很努力的向前爬...

tt.run()
# 我正在飛快的向前跑...

tt.bite()
# 咬死你咬死你!!

# Python類也是物件。它們是type的例項
print(type(Turtle))
# <class 'type'>
  • 繼承:子類自動共享父類之間資料和方法的機制

【例子】

class MyList(list):
    pass


lst = MyList([1, 5, 2, 7, 8])
lst.append(9)
lst.sort()
print(lst)

# [1, 2, 5, 7, 8, 9]
  • 多型:不同物件對同一方法響應不同的行動

【例子】

class Animal:
    def run(self):
        raise AttributeError('子類必須實現這個方法')


class People(Animal):
    def run(self):
        print('人正在走')


class Pig(Animal):
    def run(self):
        print('pig is walking')


class Dog(Animal):
    def run(self):
        print('dog is running')


def func(animal):
    animal.run()


func(Pig())
# pig is walking

2.self 是什麼

  • Python 的 self 相當於 C++ 的 this 指標。

【例子】

class Test:
    def prt(self):
        print(self)
        print(self.__class__)


t = Test()
t.prt()
# <__main__.Test object at 0x000000BC5A351208>
# <class '__main__.Test'>
  • 類的方法與普通的函式只有一個特別的區別 —— 它們必須有一個額外的第一個引數名稱(對應於該例項,即該物件本身),按照慣例它的名稱self。在呼叫方法時,我們無需明確提供與引數 self 相對應的引數。
    【例子】
class Ball:
    def setName(self, name):
        self.name = name

    def kick(self):
        print("我叫%s,該死的,誰踢我..." % self.name)


a = Ball()
a.setName("球A")
b = Ball()
b.setName("球B")
c = Ball()
c.setName("球C")
a.kick()
# 我叫球A,該死的,誰踢我...
b.kick()
# 我叫球B,該死的,誰踢我...

Python 的魔法方法

  • 類有一個名為__init__(self[, param1, param2…])的魔法方法,該方法在類例項化時會自動呼叫。

【例子】

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

    def kick(self):
        print("我叫%s,該死的,誰踢我..." % self.name)


a = Ball("球A")
b = Ball("球B")
c = Ball("球C")
a.kick()
# 我叫球A,該死的,誰踢我...
b.kick()
# 我叫球B,該死的,誰踢我...

4.公有和私有

  • 在 Python 中定義私有變數只需要在變數名或函式名前加上“__”兩個下劃線,那麼這個函式或變數就會為私有的了。

【例子】類的私有屬性例項

class JustCounter:
    __secretCount = 0  # 私有變數
    publicCount = 0  # 公開變數

    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print(self.__secretCount)


counter = JustCounter()
counter.count()  # 1
counter.count()  # 2
print(counter.publicCount)  # 2

# Python的私有為偽私有
print(counter._JustCounter__secretCount)  # 2 
print(counter.__secretCount)  
# AttributeError: 'JustCounter' object has no attribute '__secretCount' 

【例子】類的私有方法例項

class Site:
    def __init__(self, name, url):
        self.name = name  # public
        self.__url = url  # private

    def who(self):
        print('name  : ', self.name)
        print('url : ', self.__url)

    def __foo(self):  # 私有方法
        print('這是私有方法')

    def foo(self):  # 公共方法
        print('這是公共方法')
        self.__foo()


x = Site('老馬的程式人生', 'https://blog.csdn.net/LSGO_MYP')
x.who()
# name  :  老馬的程式人生
# url :  https://blog.csdn.net/LSGO_MYP

x.foo()
# 這是公共方法
# 這是私有方法

x.__foo()
# AttributeError: 'Site' object has no attribute '__foo'

5.繼承

  • Python 同樣支援類的繼承,派生類的定義如下所示:
class DerivedClassName(BaseClassName):
       statement-1
              .
              .
              .
       statement-N
  • BaseClassName(基類名)必須與派生類定義在一個作用域內。除了類,還可以用表示式,基類定義在另一個模組中時這一點非常有用:
class DerivedClassName(modname.BaseClassName):
       statement-1
              .
              .
              .
       statement-N

【例子】如果子類中定義與父類同名的方法或屬性,則會自動覆蓋父類對應的方法或屬性。

# 類定義
class people:
    # 定義基本屬性
    name = ''
    age = 0
    # 定義私有屬性,私有屬性在類外部無法直接進行訪問
    __weight = 0

    # 定義構造方法
    def __init__(self, n, a, w):
        self.name = n
        self.age = a
        self.__weight = w

    def speak(self):
        print("%s 說: 我 %d 歲。" % (self.name, self.age))


# 單繼承示例
class student(people):
    grade = ''

    def __init__(self, n, a, w, g):
        # 呼叫父類的構函
        people.__init__(self, n, a, w)
        self.grade = g

    # 覆寫父類的方法
    def speak(self):
        print("%s 說: 我 %d 歲了,我在讀 %d 年級" % (self.name, self.age, self.grade))


s = student('小馬的程式人生', 10, 60, 3)
s.speak()
# 小馬的程式人生 說: 我 10 歲了,我在讀 3 年級

6.組合

【例子】

class Turtle:
    def __init__(self, x):
        self.num = x


class Fish:
    def __init__(self, x):
        self.num = x


class Pool:
    def __init__(self, x, y):
        self.turtle = Turtle(x)
        self.fish = Fish(y)

    def print_num(self):
        print("水池裡面有烏龜%s只,小魚%s條" % (self.turtle.num, self.fish.num))


p = Pool(2, 3)
p.print_num()
# 水池裡面有烏龜2只,小魚3條

8.什麼是繫結

  • Python 嚴格要求方法需要有例項才能被呼叫,這種限制其實就是 Python 所謂的繫結概念。
  • Python 物件的資料屬性通常儲存在名為.__ dict__的字典中,我們可以直接訪問__dict__,或利用 Python的內建函式vars()獲取.__ dict__。

【例子】

class CC:
    def setXY(self, x, y):
        self.x = x
        self.y = y

    def printXY(self):
        print(self.x, self.y)


dd = CC()
print(dd.__dict__)
# {}

print(vars(dd))
# {}

print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000C3473DA048>, 'printXY': <function CC.printXY at 0x000000C3473C4F28>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}

dd.setXY(4, 5)
print(dd.__dict__)
# {'x': 4, 'y': 5}

print(vars(CC))
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None}

print(CC.__dict__)
# {'__module__': '__main__', 'setXY': <function CC.setXY at 0x000000632CA9B048>, 'printXY': <function CC.printXY at 0x000000632CA83048>, '__dict__': <attribute '__dict__' of 'CC' objects>, '__weakref__': <attribute '__weakref__' of 'CC' objects>, '__doc__': None} 

9.一些相關的內建函式(BIF)

  • issubclass(class, classinfo) 方法用於判斷引數 class 是否是型別引數 classinfo 的子類。
  • 一個類被認為是其自身的子類。
  • classinfo可以是類物件的元組,只要class是其中任何一個候選類的子類,則返回True。

【例子】

class A:
    pass


class B(A):
    pass


print(issubclass(B, A))  # True
print(issubclass(B, B))  # True
print(issubclass(A, B))  # False
print(issubclass(B, object))  # True
  • isinstance(object, classinfo) 方法用於判斷一個物件是否是一個已知的型別,類似type()。
  • type()不會認為子類是一種父類型別,不考慮繼承關係。
  • isinstance()會認為子類是一種父類型別,考慮繼承關係。
    如果第一個引數不是物件,則永遠返回False。
    如果第二個引數不是類或者由類物件組成的元組,會丟擲一個TypeError異常。

【例子】

a = 2
print(isinstance(a, int))  # True
print(isinstance(a, str))  # False
print(isinstance(a, (str, int, list)))  # True


class A:
    pass


class B(A):
    pass


print(isinstance(A(), A))  # True
print(type(A()) == A)  # True
print(isinstance(B(), A))  # True
print(type(B()) == A)  # False
  • hasattr(object, name)用於判斷物件是否包含對應的屬性。
    【例子】
class Coordinate:
    x = 10
    y = -5
    z = 0


point1 = Coordinate()
print(hasattr(point1, 'x'))  # True
print(hasattr(point1, 'y'))  # True
print(hasattr(point1, 'z'))  # True
print(hasattr(point1, 'no'))  # False
  • getattr(object, name[, default])用於返回一個物件屬性值。
    【例子】
class A(object):
    bar = 1


a = A()
print(getattr(a, 'bar'))  # 1
print(getattr(a, 'bar2', 3))  # 3
print(getattr(a, 'bar2'))
# AttributeError: 'A' object has no attribute 'bar2'
  • setattr(object, name, value)對應函式 getattr(),用於設定屬性值,該屬性不一定是存在的。
    【例子】
class A(object):
    bar = 1


a = A()
print(getattr(a, 'bar'))  # 1
setattr(a, 'bar', 5)
print(a.bar)  # 5
setattr(a, "age", 28)
print(a.age)  # 28
  • delattr(object, name)用於刪除屬性。
    【例子】
class Coordinate:
    x = 10
    y = -5
    z = 0


point1 = Coordinate()

print('x = ', point1.x)  # x =  10
print('y = ', point1.y)  # y =  -5
print('z = ', point1.z)  # z =  0

delattr(Coordinate, 'z')

print('--刪除 z 屬性後--')  # --刪除 z 屬性後--
print('x = ', point1.x)  # x =  10
print('y = ', point1.y)  # y =  -5

# 觸發錯誤
print('z = ', point1.z)
# AttributeError: 'Coordinate' object has no attribute 'z'
  • class property([fget[, fset[, fdel[, doc]]]])用於在新式類中返回屬性值。
  • fget --獲取屬性值的函式
  • fset – 設定屬性值的函式
  • fdel – 刪除屬性值函式
  • doc – 屬性描述資訊
    【例子】
class C(object):
    def __init__(self):
        self.__x = None

    def getx(self):
        return self.__x

    def setx(self, value):
        self.__x = value

    def delx(self):
        del self.__x

    x = property(getx, setx, delx, "I'm the 'x' property.")


cc = C()
cc.x = 2
print(cc.x)  # 2

del cc.x
print(cc.x)
# AttributeError: 'C' object has no attribute '_C__x'

4、魔法方法

  • 魔法方法總是被雙下劃線包圍,例如__init__。
  • 魔法方法是物件導向的 Python 的一切,如果你不知道
  • 魔法方法,說明你還沒能意識到物件導向的 Python 的強大。
  • 魔法方法的“魔力”體現在它們總能夠在適當的時候被自動呼叫。
  • 魔法方法的第一個引數應為cls(類方法) 或者self(例項方法)。
    cls:代表一個類的名稱
    self:代表一個例項物件的名稱

1.基本的魔法方法

  • init(self[, …]) 構造器,當一個例項被建立的時候呼叫的初始化方法
    【例子】
class Rectangle:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def getPeri(self):
        return (self.x + self.y) * 2

    def getArea(self):
        return self.x * self.y


rect = Rectangle(4, 5)
print(rect.getPeri())  # 18
print(rect.getArea())  # 20
  • new_(cls[, …]) 在一個物件例項化的時候所呼叫的第一個方法,在呼叫__init__初始化前,先呼叫__new__
  • new__至少要有一個引數cls,代表要例項化的類,此引數在例項化時由 Python 直譯器自動提供,後面的引數直接傳遞給__init
  • new__對當前類進行了例項化,並將例項返回,傳給__init__的self。但是,執行了__new,並不一定會進入__init__,只有__new__返回了,當前類cls的例項,當前類的__init__才會進入。
    【例子】
class A(object):
    def __init__(self, value):
        print("into A __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into A __new__")
        print(cls)
        return object.__new__(cls)


class B(A):
    def __init__(self, value):
        print("into B __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into B __new__")
        print(cls)
        return super().__new__(cls, *args, **kwargs)


b = B(10)

# 結果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.B'>
# into B __init__

class A(object):
    def __init__(self, value):
        print("into A __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into A __new__")
        print(cls)
        return object.__new__(cls)


class B(A):
    def __init__(self, value):
        print("into B __init__")
        self.value = value

    def __new__(cls, *args, **kwargs):
        print("into B __new__")
        print(cls)
        return super().__new__(A, *args, **kwargs)  # 改動了cls變為A


b = B(10)

# 結果:
# into B __new__
# <class '__main__.B'>
# into A __new__
# <class '__main__.A'>
  • 若__new__沒有正確返回當前類cls的例項,那__init__是不會被呼叫的,即使是父類的例項也不行,將沒有__init__被呼叫。

【例子】利用__new__實現單例模式。

class Earth:
    pass


a = Earth()
print(id(a))  # 260728291456
b = Earth()
print(id(b))  # 260728291624

class Earth:
    __instance = None  # 定義一個類屬性做判斷

    def __new__(cls):
        if cls.__instance is None:
            cls.__instance = object.__new__(cls)
            return cls.__instance
        else:
            return cls.__instance


a = Earth()
print(id(a))  # 512320401648
b = Earth()
print(id(b))  # 512320401648
  • __new__方法主要是當你繼承一些不可變的 class 時(比如int, str, tuple), 提供給你一個自定義這些類的例項化過程的途徑。
    【例子】
class CapStr(str):
    def __new__(cls, string):
        string = string.upper()
        return str.__new__(cls, string)


a = CapStr("i love lsgogroup")
print(a)  # I LOVE LSGOGROUP
  • del(self) 析構器,當一個物件將要被系統回收之時呼叫的方法。
    【例子】
class C(object):
    def __init__(self):
        print('into C __init__')

    def __del__(self):
        print('into C __del__')


c1 = C()
# into C __init__
c2 = c1
c3 = c2
del c3
del c2
del c1
# into C __del__
  • str(self):
  • 當你列印一個物件的時候,觸發__str__
  • 當你使用%s格式化的時候,觸發__str__
  • str強轉資料型別的時候,觸發__str__
  • repr(self):
  • repr是str的備胎
  • 有__str__的時候執行__str__,沒有實現__str__的時候,執行__repr__
  • repr(obj)內建函式對應的結果是__repr__的返回值
  • 當你使用%r格式化的時候 觸發__repr__
    【例子】
class Cat:
    """定義一個貓類"""

    def __init__(self, new_name, new_age):
        """在建立完物件之後 會自動呼叫, 它完成物件的初始化的功能"""
        self.name = new_name
        self.age = new_age

    def __str__(self):
        """返回一個物件的描述資訊"""
        return "名字是:%s , 年齡是:%d" % (self.name, self.age)
        
    def __repr__(self):
        """返回一個物件的描述資訊"""
        return "Cat:(%s,%d)" % (self.name, self.age)

    def eat(self):
        print("%s在吃魚...." % self.name)

    def drink(self):
        print("%s在喝可樂..." % self.name)

    def introduce(self):
        print("名字是:%s, 年齡是:%d" % (self.name, self.age))


# 建立了一個物件
tom = Cat("湯姆", 30)
print(tom)  # 名字是:湯姆 , 年齡是:30
print(str(tom)) # 名字是:湯姆 , 年齡是:30
print(repr(tom))  # Cat:(湯姆,30)
tom.eat()  # 湯姆在吃魚....
tom.introduce()  # 名字是:湯姆, 年齡是:30
  • str(self) 的返回結果可讀性強。也就是說,str 的意義是得到便於人們閱讀的資訊,就像下面的 ‘2019-10-11’ 一樣。
  • repr(self) 的返回結果應更準確。怎麼說,repr 存在的目的在於除錯,便於開發者使用。

【例子】

import datetime

today = datetime.date.today()
print(str(today))  # 2019-10-11
print(repr(today))  # datetime.date(2019, 10, 11)
print('%s' %today)  # 2019-10-11
print('%r' %today)  # datetime.date(2019, 10, 11)

2.算術運算子

  • 型別工廠函式,指的是“不通過類而是通過函式來建立物件”。

【例子】

class C:
    pass


print(type(len))  # <class 'builtin_function_or_method'>
print(type(dir))  # <class 'builtin_function_or_method'>
print(type(int))  # <class 'type'>
print(type(list))  # <class 'type'>
print(type(tuple))  # <class 'type'>
print(type(C))  # <class 'type'>
print(int('123'))  # 123

# 這個例子中list工廠函式把一個元祖物件加工成了一個列表物件。
print(list((1, 2, 3)))  # [1, 2, 3]
  • add(self, other)定義加法的行為:+
  • sub(self, other)定義減法的行為:-
    【例子】
class MyClass:

    def __init__(self, height, weight):
        self.height = height
        self.weight = weight

    # 兩個物件的長相加,寬不變.返回一個新的類
    def __add__(self, others):
        return MyClass(self.height + others.height, self.weight + others.weight)

    # 兩個物件的寬相減,長不變.返回一個新的類
    def __sub__(self, others):
        return MyClass(self.height - others.height, self.weight - others.weight)

    # 說一下自己的引數
    def intro(self):
        print("高為", self.height, " 重為", self.weight)


def main():
    a = MyClass(height=10, weight=5)
    a.intro()

    b = MyClass(height=20, weight=10)
    b.intro()

    c = b - a
    c.intro()

    d = a + b
    d.intro()


if __name__ == '__main__':
    main()

# 高為 10  重為 5
# 高為 20  重為 10
# 高為 10  重為 5
# 高為 30  重為 15
  • mul(self, other)定義乘法的行為:*
  • truediv(self, other)定義真除法的行為:/
  • floordiv(self, other)定義整數除法的行為://
  • mod(self, other) 定義取模演算法的行為:%
  • divmod(self, other)定義當被 divmod() 呼叫時的行為 divmod(a, b)把除數和餘數運算結果結合起來,返回一個包含商和餘數的元組(a // b, a % b)。
    【例子】
print(divmod(7, 2))  # (3, 1)
print(divmod(8, 2))  # (4, 0)
  • pow(self, other[, module])定義當被 power() 呼叫或 ** 運算時的行為
  • lshift(self, other)定義按位左移位的行為:<<
  • rshift(self, other)定義按位右移位的行為:>>
  • and(self, other)定義按位與操作的行為:&
  • xor(self, other)定義按位異或操作的行為:^
  • or(self, other)定義按位或操作的行為:|

3.反算術運算子

反運算魔方方法,與算術運算子保持一一對應,不同之處就是反運算的魔法方法多了一個“r”。當檔案左操作不支援相應的操作時被呼叫。

  • radd(self, other)定義加法的行為:+
  • rsub(self, other)定義減法的行為:-
  • rmul(self, other)定義乘法的行為:*
  • rtruediv(self, other)定義真除法的行為:/
  • rfloordiv(self, other)定義整數除法的行為://
  • rmod(self, other) 定義取模演算法的行為:%
  • rdivmod(self, other)定義當被 divmod() 呼叫時的行
  • rpow(self, other[, module])定義當被 power() 呼叫或 ** 運算時的行為
  • rlshift(self, other)定義按位左移位的行為:<<
  • rrshift(self, other)定義按位右移位的行為:>>
  • rand(self, other)定義按位與操作的行為:&
  • rxor(self, other)定義按位異或操作的行為:^
  • ror(self, other)定義按位或操作的行為:|
    【例子】
class Nint(int):
    def __radd__(self, other):
        return int.__sub__(other, self) # 注意 self 在後面


a = Nint(5)
b = Nint(3)
print(a + b)  # 8
print(1 + b)  # -2

4.增量賦值運算子

  • iadd(self, other)定義賦值加法的行為:+=
  • isub(self, other)定義賦值減法的行為:-=
  • imul(self, other)定義賦值乘法的行為:*=
  • itruediv(self, other)定義賦值真除法的行為:/=
  • ifloordiv(self, other)定義賦值整數除法的行為://=
  • imod(self, other)定義賦值取模演算法的行為:%=
  • ipow(self, other[, modulo])定義賦值冪運算的行為:**=
  • ilshift(self, other)定義賦值按位左移位的行為:<<=
  • irshift(self, other)定義賦值按位右移位的行為:>>=
  • iand(self, other)定義賦值按位與操作的行為:&=
  • ixor(self, other)定義賦值按位異或操作的行為:^=
  • ior(self, other)定義賦值按位或操作的行為:|=

5.一元運算子

  • neg(self)定義正號的行為:+x
  • pos(self)定義負號的行為:-x
  • abs(self)定義當被abs()呼叫時的行為
  • invert(self)定義按位求反的行為:~x

6.屬性訪問

  • getattr(self, name): 定義當使用者試圖獲取一個不存在的屬性時的行為。
  • getattribute(self, name):定義當該類的屬性被訪問時的行為(先呼叫該方法,檢視是否存在該屬性,若不存在,接著去呼叫__getattr__)。
  • setattr(self, name, value):定義當一個屬性被設定時的行為。
  • delattr(self, name):定義當一個屬性被刪除時的行為。

7.描述符

描述符就是將某種特殊型別的類的例項指派給另一個類的屬性。

  • get(self, instance, owner)用於訪問屬性,它返回屬性的值。
  • set(self, instance, value)將在屬性分配操作中呼叫,不返回任何內容。
  • del(self, instance)控制刪除操作,不返回任何內容。
    【例子】
class MyDecriptor:
    def __get__(self, instance, owner):
        print('__get__', self, instance, owner)

    def __set__(self, instance, value):
        print('__set__', self, instance, value)

    def __delete__(self, instance):
        print('__delete__', self, instance)


class Test:
    x = MyDecriptor()


t = Test()
t.x
# __get__ <__main__.MyDecriptor object at 0x000000CEAAEB6B00> <__main__.Test object at 0x000000CEABDC0898> <class '__main__.Test'>

t.x = 'x-man'
# __set__ <__main__.MyDecriptor object at 0x00000023687C6B00> <__main__.Test object at 0x00000023696B0940> x-man

del t.x
# __delete__ <__main__.MyDecriptor object at 0x000000EC9B160A90> <__main__.Test object at 0x000000EC9B160B38>

8.定製序列

協議(Protocols)與其它程式語言中的介面很相似,它規定你哪些方法必須要定義。然而,在 Python 中的協議就顯得不那麼正式。事實上,在 Python 中,協議更像是一種指南。

容器型別的協議

  • 如果說你希望定製的容器是不可變的話,你只需要定義__len__()和__getitem__()方法。
  • 如果你希望定製的容器是可變的話,除了__len__()和__getitem__()方法,你還需要定義__setitem__()和__delitem__()兩個方法。

【例子】編寫一個不可改變的自定義列表,要求記錄列表中每個元素被訪問的次數。

class CountList:
    def __init__(self, *args):
        self.values = [x for x in args]
        self.count = {}.fromkeys(range(len(self.values)), 0)

    def __len__(self):
        return len(self.values)

    def __getitem__(self, item):
        self.count[item] += 1
        return self.values[item]


c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1])  # 3
print(c2[2])  # 6
print(c1[1] + c2[1])  # 7

print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}

print(c2.count)
# {0: 0, 1: 1, 2: 1, 3: 0, 4: 0}
  • len(self)定義當被len()呼叫時的行為(返回容器中元素的個數)
  • getitem(self, key)定義獲取容器中元素的行為,相當於self[key]。
  • setitem(self, key, value)定義設定容器中指定元素的行為,相當於self[key] = value。
  • delitem(self, key)定義刪除容器中指定元素的行為,相當於del self[key]。

【例子】編寫一個可改變的自定義列表,要求記錄列表中每個元素被訪問的次數。

class CountList:
    def __init__(self, *args):
        self.values = [x for x in args]
        self.count = {}.fromkeys(range(len(self.values)), 0)

    def __len__(self):
        return len(self.values)

    def __getitem__(self, item):
        self.count[item] += 1
        return self.values[item]

    def __setitem__(self, key, value):
        self.values[key] = value

    def __delitem__(self, key):
        del self.values[key]
        for i in range(0, len(self.values)):
            if i >= key:
                self.count[i] = self.count[i + 1]
        self.count.pop(len(self.values))


c1 = CountList(1, 3, 5, 7, 9)
c2 = CountList(2, 4, 6, 8, 10)
print(c1[1])  # 3
print(c2[2])  # 6
c2[2] = 12
print(c1[1] + c2[2])  # 15
print(c1.count)
# {0: 0, 1: 2, 2: 0, 3: 0, 4: 0}
print(c2.count)
# {0: 0, 1: 0, 2: 2, 3: 0, 4: 0}
del c1[1]
print(c1.count)
# {0: 0, 1: 0, 2: 0, 3: 0}

9.迭代器

  • 迭代是 Python 最強大的功能之一,是訪問集合元素的一種方式。
  • 迭代器是一個可以記住遍歷的位置的物件。
  • 迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。
  • 迭代器只能往前不會後退。 字串,列表或元組物件都可用於建立迭代器:
    【例子】
string = 'lsgogroup'
for c in string:
    print(c)

'''
l
s
g
o
g
r
o
u
p
'''

for c in iter(string):
    print(c)
  • 迭代器有兩個基本的方法:iter() 和 next()。
  • iter(object) 函式用來生成迭代器。
  • next(iterator[,default]) 返回迭代器的下一個專案。
  • iterator – 可迭代物件
  • default --可選,用於設定在沒有下一個元素時返回該預設值,如果不設定,又沒有下一個元素則會觸發 StopIteration 異常。

【例子】

links = {'B': '百度', 'A': '阿里', 'T': '騰訊'}

it = iter(links)
while True:
    try:
        each = next(it)
    except StopIteration:
        break
    print(each)

# B
# A
# T

it = iter(links)
print(next(it))  # B
print(next(it))  # A
print(next(it))  # T
print(next(it))  # StopIteration

把一個類作為一個迭代器使用需要在類中實現兩個魔法方法 iter() 與 next() 。

  • iter(self)定義當迭代容器中的元素的行為,返回一個特殊的迭代器物件, 這個迭代器物件實現了
  • next() 方法並通過 StopIteration 異常標識迭代的完成。next() 返回下一個迭代器物件。
  • StopIteration 異常用於標識迭代的完成,防止出現無限迴圈的情況,在 next() 方法中我們可以設定在完成指定迴圈次數後觸發 StopIteration 異常來結束迭代。

【例子】

class Fibs:
    def __init__(self, n=10):
        self.a = 0
        self.b = 1
        self.n = n

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > self.n:
            raise StopIteration
        return self.a


fibs = Fibs(100)
for each in fibs:
    print(each, end=' ')

# 1 1 2 3 5 8 13 21 34 55 89

生成器

  • 在 Python 中,使用了 yield 的函式被稱為生成器(generator)。
  • 跟普通函式不同的是,生成器是一個返回迭代器的函式,只能用於迭代操作,更簡單點理解生成器就是一個迭代器。
  • 在呼叫生成器執行的過程中,每次遇到yield 時函式會暫停並儲存當前所有的執行資訊,返回 yield 的值, 並在下一次執行 next() 方法時從當前位置繼續執行。
  • 呼叫一個生成器函式,返回的是一個迭代器物件。

【例子】

def myGen():
    print('生成器執行!')
    yield 1
    yield 2
    
myG = myGen()
for each in myG:
    print(each)

'''
生成器執行!
1
2
'''

myG = myGen()
print(next(myG))  
# 生成器執行!
# 1

print(next(myG))  # 2
print(next(myG))  # StopIteration

相關文章