日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

Python学习笔记:面向对象高级编程(中上)

發(fā)布時(shí)間:2025/3/15 python 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Python学习笔记:面向对象高级编程(中上) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

最近在學(xué)習(xí)深度學(xué)習(xí),已經(jīng)跑出了幾個(gè)模型,但Pyhton的基礎(chǔ)不夠扎實(shí),因此,開(kāi)始補(bǔ)習(xí)Python了,大家都推薦廖雪峰的課程,因此,開(kāi)始了學(xué)習(xí),但光學(xué)有沒(méi)有用,還要和大家討論一下,因此,寫(xiě)下這些帖子,廖雪峰的課程連接在這里:廖雪峰
Python的相關(guān)介紹,以及它的歷史故事和運(yùn)行機(jī)制,可以參見(jiàn)這篇:python介紹
Python的安裝可以參見(jiàn)這篇:Python安裝
Python的運(yùn)行模式以及輸入輸出可以參見(jiàn)這篇:Python IO
Python的基礎(chǔ)概念介紹,可以參見(jiàn)這篇:Python 基礎(chǔ)
Python字符串和編碼的介紹,可以參見(jiàn)這篇:Python字符串與編碼
Python基本數(shù)據(jù)結(jié)構(gòu):list和tuple介紹,可以參見(jiàn)這篇:Python list和tuple
Python控制語(yǔ)句介紹:ifelse,可以參見(jiàn)這篇:Python 條件判斷
Python控制語(yǔ)句介紹:循環(huán)實(shí)現(xiàn),可以參見(jiàn)這篇:Python循環(huán)語(yǔ)句
Python數(shù)據(jù)結(jié)構(gòu):dict和set介紹Python數(shù)據(jù)結(jié)構(gòu)dict和set
Python函數(shù)相關(guān):Python函數(shù)
Python高階特性:Python高級(jí)特性
Python高階函數(shù):Python高階函數(shù)
Python匿名函數(shù):Python匿名函數(shù)
Python裝飾器:Python裝飾器
Python偏函數(shù):Python偏函數(shù)
Python模塊:Python模塊
Python面向?qū)ο缶幊?#xff08;1):Python面向?qū)ο?
Python面向?qū)ο缶幊?#xff08;2):Python面向?qū)ο?#xff08;2)
Python面向?qū)ο缶幊?#xff08;3):Python面向?qū)ο?#xff08;3)
Python面向?qū)ο缶幊?#xff08;4):Pyhton面向?qū)ο?#xff08;4)
Python面向?qū)ο蟾呒?jí)編程(上):Python面向?qū)ο蟾呒?jí)編程(上)
目錄:

  • 前言
  • 定制類(lèi)
    • _str_
    • _iter_
  • _getitem_
    • _getattr_
    • _call_
    • 小結(jié)

定制類(lèi)

看到類(lèi)似_slots_這種形如_xxx_的變量或者函數(shù)名就要注意,這些在Python中是有特殊用途的。_slots_我們已經(jīng)知道怎么用了,_len_()方法我們也知道是為了能讓class具有l(wèi)en()方法。
除此之外,Python的class中還有許多這樣有特殊用途的函數(shù),可以幫助我們定制類(lèi)。

_str_

我們先定義一個(gè)Student類(lèi),打印一個(gè)實(shí)例:

>>> class Student(object):def __init__(self, name):self.name = name>>> print(Student('Michael')) <__main__.Student object at 0x109afb190>

打印出一堆<main.Student object at 0x109afb190>,不好看。

怎么才能打印得好看呢?只需要定義好str()方法,返回一個(gè)好看的字符串就可以了:

>>> class Student(object): ... def __init__(self, name): ... self.name = name ... def __str__(self): ... return 'Student object (name: %s)' % self.name ... >>> print(Student('Mike')) Student object (name: Mike)

這樣打印出來(lái)的實(shí)例,不但好看,而且容易看出實(shí)例內(nèi)部重要的數(shù)據(jù)。
但是細(xì)心的朋友會(huì)發(fā)現(xiàn)直接敲變量不用print,打印出來(lái)的實(shí)例還是不好看:

>>> s = Student('Mike') >>> s <__main__.Student object at 0x109afb310>

這是因?yàn)橹苯语@示變量調(diào)用的不是_str_(),而是_repr_(),兩者的區(qū)別是_str_()返回用戶(hù)看到的字符串,而_repr_()返回程序開(kāi)發(fā)者看到的字符串,也就是說(shuō),_repr_()是為調(diào)試服務(wù)的。
解決辦法是再定義一個(gè)_repr_()。但是通常_str_()_repr_()代碼都是一樣的,所以,有個(gè)偷懶的寫(xiě)法:

class Student(object):def __init__(self, name):self.name = namedef __str__(self):return 'Student object (name=%s)' % self.name__repr__ = __str__

_iter_

如果一個(gè)類(lèi)想被用于for … in循環(huán),類(lèi)似list或tuple那樣,就必須實(shí)現(xiàn)一個(gè)_iter_()方法,該方法返回一個(gè)迭代對(duì)象,然后,Python的for循環(huán)就會(huì)不斷調(diào)用該迭代對(duì)象的_next_()方法拿到循環(huán)的下一個(gè)值,直到遇到StopIteration錯(cuò)誤時(shí)退出循環(huán)。
我們以斐波那契數(shù)列為例,寫(xiě)一個(gè)Fib類(lèi),可以作用于for循環(huán):

class Fib(object):def __init__(self):self.a, self.b = 0, 1 # 初始化兩個(gè)計(jì)數(shù)器a,bdef __iter__(self):return self # 實(shí)例本身就是迭代對(duì)象,故返回自己def __next__(self):self.a, self.b = self.b, self.a + self.b # 計(jì)算下一個(gè)值if self.a > 100000: # 退出循環(huán)的條件raise StopIteration()return self.a # 返回下一個(gè)值

現(xiàn)在,試試把Fib實(shí)例作用于for循環(huán):

>>> for n in Fib(): ... print(n) ... 1 1 2 3 5 ... 46368 75025

_getitem_

Fib實(shí)例雖然能作用于for循環(huán),看起來(lái)和list有點(diǎn)像,但是,把它當(dāng)成list來(lái)使用還是不行,比如,取第5個(gè)元素:

>>> Fib()[5] Traceback (most recent call last):File "<stdin>", line 1, in <module> TypeError: 'Fib' object does not support indexing

要表現(xiàn)得像list那樣按照下標(biāo)取出元素,需要實(shí)現(xiàn)getitem()方法:

class Fib(object):def __getitem__(self, n):a, b = 1, 1for x in range(n):a, b = b, a + breturn a

現(xiàn)在,就可以按下標(biāo)訪(fǎng)問(wèn)數(shù)列的任意一項(xiàng)了:

>>> f = Fib() >>> f[0] 1 >>> f[1] 1 >>> f[2] 2 >>> f[3] 3 >>> f[10] 89 >>> f[100] 573147844013817084101

但是list有個(gè)神奇的切片方法:

>>> list(range(100))[5:10] [5, 6, 7, 8, 9]

對(duì)于Fib卻報(bào)錯(cuò)。原因是getitem()傳入的參數(shù)可能是一個(gè)int,也可能是一個(gè)切片對(duì)象slice,所以要做判斷:

class Fib(object):def __getitem__(self, n):if isinstance(n, int): # n是索引a, b = 1, 1for x in range(n):a, b = b, a + breturn aif isinstance(n, slice): # n是切片start = n.startstop = n.stopif start is None:start = 0a, b = 1, 1L = []for x in range(stop):if x >= start:L.append(a)a, b = b, a + breturn L

現(xiàn)在試試Fib的切片:

>>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

但是沒(méi)有對(duì)step參數(shù)作處理:

>>> f[:10:2] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

也沒(méi)有對(duì)負(fù)數(shù)作處理,所以,要正確實(shí)現(xiàn)一個(gè)_getitem_()還是有很多工作要做的。

此外,如果把對(duì)象看成dict,_getitem_()的參數(shù)也可能是一個(gè)可以作key的object,例如str。

與之對(duì)應(yīng)的是_setitem_()方法,把對(duì)象視作list或dict來(lái)對(duì)集合賦值。最后,還有一個(gè)_delitem_()方法,用于刪除某個(gè)元素。

總之,通過(guò)上面的方法,我們自己定義的類(lèi)表現(xiàn)得和Python自帶的list、tuple、dict沒(méi)什么區(qū)別,這完全歸功于動(dòng)態(tài)語(yǔ)言的“鴨子類(lèi)型”,不需要強(qiáng)制繼承某個(gè)接口。

_getattr_

正常情況下,當(dāng)我們調(diào)用類(lèi)的方法或?qū)傩詴r(shí),如果不存在,就會(huì)報(bào)錯(cuò)。比如定義Student類(lèi):

class Student(object):def __init__(self):self.name = 'Mike'

調(diào)用name屬性,沒(méi)問(wèn)題,但是,調(diào)用不存在的score屬性,就有問(wèn)題了:

>>> s = Student() >>> print(s.name) Michael >>> print(s.score) Traceback (most recent call last):... AttributeError: 'Student' object has no attribute 'score'

錯(cuò)誤信息很清楚地告訴我們,沒(méi)有找到score這個(gè)attribute。

要避免這個(gè)錯(cuò)誤,除了可以加上一個(gè)score屬性外,Python還有另一個(gè)機(jī)制,那就是寫(xiě)一個(gè)getattr()方法,動(dòng)態(tài)返回一個(gè)屬性。修改如下:

class Student(object):def __init__(self):self.name = 'Michael'def __getattr__(self, attr):if attr=='score':return 99

當(dāng)調(diào)用不存在的屬性時(shí),比如score,Python解釋器會(huì)試圖調(diào)用getattr(self, ‘score’)來(lái)嘗試獲得屬性,這樣,我們就有機(jī)會(huì)返回score的值:

>>> s = Student() >>> s.name 'Michael' >>> s.score 99

返回函數(shù)也是完全可以的:

class Student(object):def __getattr__(self, attr):if attr=='age':return lambda: 25

只是調(diào)用方式要變?yōu)?#xff1a;

>>> s.age() 25

注意,只有在沒(méi)有找到屬性的情況下,才調(diào)用getattr,已有的屬性,比如name,不會(huì)在getattr中查找。

此外,注意到任意調(diào)用如s.abc都會(huì)返回None,這是因?yàn)槲覀兌x的getattr默認(rèn)返回就是None。要讓class只響應(yīng)特定的幾個(gè)屬性,我們就要按照約定,拋出AttributeError的錯(cuò)誤:

class Student(object):def __getattr__(self, attr):if attr=='age':return lambda: 25raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

這實(shí)際上可以把一個(gè)類(lèi)的所有屬性和方法調(diào)用全部動(dòng)態(tài)化處理了,不需要任何特殊手段。

這種完全動(dòng)態(tài)調(diào)用的特性有什么實(shí)際作用呢?作用就是,可以針對(duì)完全動(dòng)態(tài)的情況作調(diào)用。

舉個(gè)例子:

現(xiàn)在很多網(wǎng)站都搞REST API,比如新浪微博、豆瓣啥的,調(diào)用API的URL類(lèi)似:

http://api.server/user/friendshttp://api.server/user/timeline/list

如果要寫(xiě)SDK,給每個(gè)URL對(duì)應(yīng)的API都寫(xiě)一個(gè)方法,那得累死,而且,API一旦改動(dòng),SDK也要改。

利用完全動(dòng)態(tài)的_getattr_,我們可以寫(xiě)出一個(gè)鏈?zhǔn)秸{(diào)用:

class Chain(object):def __init__(self, path=''):self._path = pathdef __getattr__(self, path):return Chain('%s/%s' % (self._path, path))def __str__(self):return self._path__repr__ = __str__

試試:

>>> Chain().status.user.timeline.list '/status/user/timeline/list'

這樣,無(wú)論API怎么變,SDK都可以根據(jù)URL實(shí)現(xiàn)完全動(dòng)態(tài)的調(diào)用,而且,不隨API的增加而改變!

還有些REST API會(huì)把參數(shù)放到URL中,比如GitHub的API:

GET /users/:user/repos

調(diào)用時(shí),需要把:user替換為實(shí)際用戶(hù)名。如果我們能寫(xiě)出這樣的鏈?zhǔn)秸{(diào)用:

Chain().users('michael').repos

就可以非常方便地調(diào)用API了。有興趣的童鞋可以試試寫(xiě)出來(lái)。

_call_

一個(gè)對(duì)象實(shí)例可以有自己的屬性和方法,當(dāng)我們調(diào)用實(shí)例方法時(shí),我們用instance.method()來(lái)調(diào)用。能不能直接在實(shí)例本身上調(diào)用呢?在Python中,答案是肯定的。

任何類(lèi),只需要定義一個(gè)_call_()方法,就可以直接對(duì)實(shí)例進(jìn)行調(diào)用。請(qǐng)看示例:

class Student(object):def __init__(self, name):self.name = namedef __call__(self):print('My name is %s.' % self.name)

調(diào)用方式如下:

>>> s = Student('Michael') >>> s() # self參數(shù)不要傳入 My name is Michael.

_call_()還可以定義參數(shù)。對(duì)實(shí)例進(jìn)行直接調(diào)用就好比對(duì)一個(gè)函數(shù)進(jìn)行調(diào)用一樣,所以你完全可以把對(duì)象看成函數(shù),把函數(shù)看成對(duì)象,因?yàn)檫@兩者之間本來(lái)就沒(méi)啥根本的區(qū)別。

如果你把對(duì)象看成函數(shù),那么函數(shù)本身其實(shí)也可以在運(yùn)行期動(dòng)態(tài)創(chuàng)建出來(lái),因?yàn)轭?lèi)的實(shí)例都是運(yùn)行期創(chuàng)建出來(lái)的,這么一來(lái),我們就模糊了對(duì)象和函數(shù)的界限。

那么,怎么判斷一個(gè)變量是對(duì)象還是函數(shù)呢?其實(shí),更多的時(shí)候,我們需要判斷一個(gè)對(duì)象是否能被調(diào)用,能被調(diào)用的對(duì)象就是一個(gè)Callable對(duì)象,比如函數(shù)和我們上面定義的帶有_call_()的類(lèi)實(shí)例:

>>> callable(Student) True >>> callable(max) True >>> callable([1, 2, 3]) False >>> callable(None) False >>> callable('str') False

通過(guò)callable()函數(shù),我們就可以判斷一個(gè)對(duì)象是否是“可調(diào)用”對(duì)象。

小結(jié)

小結(jié)
Python的class允許定義許多定制方法,可以讓我們非常方便地生成特定的類(lèi)。
本節(jié)介紹的是最常用的幾個(gè)定制方法,還有很多可定制的方法,請(qǐng)參考Python官方文檔

總結(jié)

以上是生活随笔為你收集整理的Python学习笔记:面向对象高级编程(中上)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。