Python中級 —— 01面向對象進階

来源:https://www.cnblogs.com/darksouls/archive/2018/01/16/8299463.html
-Advertisement-
Play Games

面向對象進階 面向對象高級語法部分 靜態方法、類方法、屬性方法 類的特殊方法 反射 異常處理 1、靜態方法(@staticmethod) 通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。 普通的方法,可以在實例化後直接調用,並且在方法里可以通過self.調用實例變數或類變數, ...


面向對象進階

總結、補充(http://blog.csdn.net/fgf00/article/details/52479307

面向對象高級語法部分

  靜態方法、類方法、屬性方法

類的特殊方法

反射

動態綁定屬性,限制綁定( __slots__

異常處理

一、 面向對象高級語法部分

1、靜態方法(@staticmethod)

通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。
普通的方法,可以在實例化後直接調用,並且在方法里可以通過self.調用實例變數或類變數,但靜態方法是不可以訪問實例變數或類變數的,
一個不能訪問實例變數和類變數的方法,其實相當於跟類本身已經沒什麼關係了,它與類唯一的關聯就是需要通過類名來調用這個方法.
class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @staticmethod  # 把eat方法變為靜態方法
    def eat(self):
        print("%s is eating" %self.name)

d = Person("xiaoming", 18)
d.eat()

上面的調用會出以下 錯誤

TypeError: eat() missing 1 required positional argument: 'self'
解決方法(2種):
    1. 調用時主動傳遞實例本身給eat方法,即 `d.eat(d)`
    2. 在eat方法中去掉self參數,但這也意味著,在eat中不能通過self.調用實例中的其它變數了   

作用:只是相當於一個單純函數,要傳參數,就要把實例傳進去。 
如果說和類有關係,就是必須有類名去調用。調用不了類或實例中的任何屬性

2、類方法(@classmethod)

類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是: 
類方法只能訪問類變數,不能訪問實例變數
class Person(object):
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @classmethod  
    def eat(self):
        print("%s is eating" %self.name)

d = Person("xiaoming", 18)
d.eat()
執行報錯如下, ` AttributeError: type object 'Dog' has no attribute 'name' ` ,Dog沒有name屬性,因為name是個實例變數,類方法是不能訪問實例變數的   

此時可以定義一個類變數,也叫name,看下執行效果:
class Person(object):
    name = "xiaohong" # 類變數
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @classmethod  
    def eat(self):
        print("%s is eating" %self.name)

d = Person("xiaoming", 18)
d.eat()

3、屬性方法(@property)

屬性方法的作用就是通過@property把一個方法變成一個靜態屬性 (函數–>變數)
class Person(object):
    name = "xiaohong" # 類變數
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @property  
    def eat(self):
        print("%s is eating" %self.name)

d = Person("xiaoming", 18)
d.eat()
可能報錯  ` TypeError: 'NoneType' object is not callable `     
    正常調用如下:

d.eat
# 輸出
xiaoming is eating

傳參:
屬性方法賦值:
d.eat = "baozi" 多個參數時: d.eat = "baozi", "baozi2" 接收為元組形式
刪除屬性方法:
del d.eat 報錯: AttributeError: can't delete attribute
類中定義以下方法即可:

@eat.deleter  # 刪除屬性
def eat(self):
    del self.__food
    print("Delete the finished")

此時代碼:

class Person(object):
    name = "xiaohong" # 類變數
    def __init__(self,name,age):
        self.name = name
        self.age = age
    @property  
    def eat(self):
        print("%s is eating" %self.name)
    @eat.setter  # 賦值調用屬性,調這個方法
    def eat(self,food):
        print("set to food:",food)
        self.__food = food
    @eat.deleter  # 刪除屬性
    def eat(self):
        del self.__food
        print("Delete the finished")

d = Person("xiaoming", 18)
d.eat()
d.eat = "baozi"
d.eat  # 傳完參數後調用
del d.eat
d.eat  # 刪完後調用

此時報錯: AttributeError: 'Dog' object has no attribute '_Dog__food' ,說明已刪除。

好吧,把一個方法變成靜態屬性有什麼卵用呢?既然想要靜態變數,那直接定義成一個靜態變數不就得了麽?well, 以後你會需到很多場景是不能簡單通過 定義 靜態屬性來實現的, 比如 ,你想知道一個航班當前的狀態,是到達了、延遲了、取消了、還是已經飛走了, 想知道這種狀態你必須經歷以下幾步:

  1. 連接航空公司API查詢
  2. 對查詢結果進行解析
  3. 返回結果給你的用戶

因此這個status屬性的值是一系列動作後才得到的結果,所以你每次調用時,其實它都要經過一系列的動作才返回你結果,但這些動作過程不需要用戶關心, 用戶只需要調用這個屬性就可以

二、類的特殊成員方法

1. __doc__  表示類的描述信息

2. __module____class__

__module__ 返回當前操作的對象對應的模塊名 
__class__ 表示當前操作的對象的類名

3. __init__ 構造方法,通過類 ** 創建 ** 對象時,自動觸發執行。

4. __call__ 對象後面加括弧 ** 執行 **,觸發執行。

5. __del__ 析構方法,當對象在記憶體中被釋放時,自動觸發執行。

註:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括弧觸發的,即:對象() 或者 類()()

6. __dict__ 查看類或對象中的所有成員

7. __str__ 如果一個類中定義了_str_方法,那麼在列印對象時,預設輸出該方法的返回值。(不定義此方法,預設返回對象地址)

8. __iter__

如果一個類想被用於for ... in迴圈,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代對象,
然後,Python的for迴圈就會不斷調用該迭代對象的 `__next__()` 方法拿到迴圈的下一個值,
直到遇到StopIteration錯誤時退出迴圈。

我們以斐波那契數列為例,寫一個Fib類,可以作用於for迴圈:
class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1 # 初始化兩個計數器a,b

    def __iter__(self):
        return self # 實例本身就是迭代對象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b # 計算下一個值
        if self.a > 100000: # 退出迴圈的條件
            raise StopIteration()
        return self.a # 返回下一個值

結果:

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

9. __getitem____setitem____delitem__

用於索引操作,如字典。以上分別表示獲取、設置、刪除數據 (可以直接用 * 對象[] * 索引)

class Foo(object):
    def __init__(self):
        self.data = {}
    def __getitem__(self, key):
        print('__getitem__',key)
        return self.data.get(key)
    def __setitem__(self, key, value):
        print('__setitem__',key,value)
        self.data[key] = value
    def __delitem__(self, key):
        print('__delitem__',key)

obj = Foo()

obj['name'] = 'fgf'   # 設置,自動觸發執行 __setitem__
print(obj.data)
print(obj['name'])     # 獲取值,自動觸發執行 __getitem__
del obj['name']        # 觸發__delitem__,只是調用那個方法,具體刪不刪看自己配置

10. 類的起源 __new____metaclass__type 元類


class Foo(object):
    def __init__(self,name):
        self.name = name
f = Foo("fgf")

上述代碼中,可知 f 是通過 Foo類 實例化的對象,萬物皆對象,其實 Foo類 也是對象
f 通過執行 Foo類 的構造方法創建, Foo類 通過執行 ** type ** 類的構造方法創建。
所以type又稱類的類。


print type(f) # 輸出:<class '__main__.Foo'>     表示,f 對象由Foo類創建
print type(Foo) # 輸出:<type 'type'>              表示,Foo類對象由 type 類創建

由此,創建類的方法就有 * 兩種 * :

  1. 普通方法(通過type創建的類,只是已經封裝好了): class Cat(object): pass

  2. 特殊方法:

# 定義方法
def func(self):
    print('hello fgf')

# 創建構造函數
def __init__(self,name,age):
    self.name = name
    self.age = age


# 創建類
Foo = type('Foo',(object,), {'talk': func, '__init__':__init__})
# type第一個參數:類名
# type第二個參數:當前類的基類(父類)
# type第三個參數:類的成員(字典形式)

f = Foo("fgf",18)
f.talk()
類是由type實例化產生,那麼type類是如何創建的?類又是如何創建對象的?
類中屬性: **  __mateclass__** ,即用於表示該類時由誰來實例化創建。
下麵可為__metaclass__設置一個type類的派生類,從而瞭解類創建的過程。

class MyType(type):
    def __init__(self, what, bases=None, dict=None):
        print("--MyType init---")
        super(MyType, self).__init__(what, bases, dict)
    def __call__(self, *args, **kwargs):
        print("--MyType call---")
        obj = self.__new__(self, *args, **kwargs)
        self.__init__(obj, *args, **kwargs)

class Foo(object):
    __metaclass__ = MyType
    def __init__(self, name):
        self.name = name
        print("Foo ---init__")
    def __new__(cls, *args, **kwargs):
        # __new__是用來創建實例的,定製類,先運行new里調用init,這裡寫,對預設的重構
        print("Foo --new--")
        # print(object.__new__(cls))
        return object.__new__(cls)  # 返回給init,cls這代表Foo,相當於對象的self
        # 調用父類的__new__方法

# 第一階段:解釋器從上到下執行代碼創建Foo類
# 第二階段:通過Foo類創建obj對象
obj = Foo("Fgf")

(預設之前應該還有個myType.new)先執行myType.init,再執行myType.call,再執行Foo.new,最後Foo.init

三、 反射

反射(實現用戶輸入字元串為類的方法)

通過字元串映射或修改程式運行時的狀態、屬性、方法, 有以下4個方法
attr –> attribute [əˈtrɪbjut] 屬性; (人或物的) 特征

(直接使用的函數)

1. hasattr(obj,name_str) :判斷object中有沒有一個name_str對應的方法或屬性,返回True或False

2. getattr(obj,name_str):根據字元串去獲取obj對應方法的記憶體地址

def getattr(object, name, default=None):
    """
    getattr(object, name[, default]) -> value
    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    """
class Foo(object):
    def __init__(self):
        self.name = 'fgf'
    def func(self):
        print(self.name,'say Hi')
        return "func"

obj = Foo()
print(getattr(obj,'func'))
getattr(obj, 'func')()  # # same as: obj.func()

3. setattr(obj,name_str,value)動態把一個屬性或函數裝到類裡面

例:對象ojb,函數 `def func1: pass`    添加: ` setattr(obj, 'func1', func1) `

4. delattr(obj, name_str) (只能)刪除setattr添加的屬性

5. isinstance(obj, cls):檢查是否obj是否是類 cls 的對象

6. issubclass(sub, super):檢查sub類是否是 super 類的派生類

7. 動態導入模塊

import importlib

__import__('import_lib.metaclass') #這是解釋器自己內部用的
#importlib.import_module('import_lib.metaclass') #與上面這句效果一樣,官方建議用這個

四、 動態綁定類成員,限制綁定(slots

class Student(object):
    pass

s = Student()

- 動態綁定類成員:

  • 動態添加實例的屬性 s.name = 'xiaoming'
    動態添加實例的函數 from types import MethodType
from types import MethodType

class Student(object):
    pass

s = Student()

def set_age(self, age):
    self.age = age

s.set_age = MethodType(set_age, s) # 給實例綁定一個方法
s.set_age(25) # 調用實例方法
s.age # 測試結果
< ** 此綁定只適用於單個實例,對其他實例無用。**

為了給所有實例都綁定方法,可以給 * class * 綁定方法:

  • 動態綁定類成員 Student.set_score = set_score
def set_score(self, score):
    self.score = score

Student.set_score = set_score

s, s2 = Student()

# s, s2都可調用set_score()
s.set_score(100)
s.score

s2.set_score(99)
s2.score

- 限制類成員 slots

class Student(object):
slots = ('name', 'age') # 用tuple定義允許綁定的屬性名稱

註意: __slots__定義的屬性僅對當前類實例起作用,對繼承的 子類 是不起作用的:
如果在子類中也定義__slots__,這樣,子類實例允許定義的屬性就是 *自身的__slots__加上父類的__slots__*

五、異常處理

在編程過程中為了增加友好性,在程式出現bug時一般不會將錯誤信息顯示給用戶,而是現實一個提示的頁面


try:
    diction = {}
    diction[1]
    names = []
    names[2]
except IndexError as e:             # python3.x 里不是逗號,都是as
    print(e)
except (KeyError,IndexError) as e:  # 採用統一處理辦法
    print(e)
except Exception as e:              # 抓住所有錯誤
    print(e)
else:                               # 沒出錯執行這個
    pass
finally:                            # 不管有沒有錯,都執行
    pass

try:
    status = 1
    if status != 0 :
        raise Exception("自定義異常")
except Exception as e:
    print(e)

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • [原創] 前幾天沒事幹看別人一直在玩微信上線的那一個跳一跳小游戲,玩著玩著老是掉下去,閑著沒事唄 就想了想做一個輔助程式的唄.不過先做的手動版的.自動版的有點麻煩.就不發了.用的Java寫的,也就一個蒙版. 下麵就開始介紹我的小程式,沒好多東西,真正的代碼應該就是100行左右,沒啥難的. 下麵這是我 ...
  • 首先,放上項目github地址:https://github.com/codethereforam/express mysql demo 一、前言 之前學的java,一直用的ssm框架寫後臺。前段時間接觸到node.js,於是花了兩天時間學了一下node.js並寫了一個CRUD簡單示例。由於前幾天一 ...
  • 終於有一點點小變化了,今天學習了java里的對話框,有四種類型:1、確認對話框(showConfirmDialog) 2、可選擇輸入的對話框(showInputDialog) 3、信息對話框(showMessageDialog) 4、可以對其他三種對話框組合的。(showOptionDialog) ...
  • http://blog.csdn.net/u011781521/article/details/70188171 http://wiki.jikexueyuan.com/project/scrapy/autothrottle.html coding: utf 8 Scrapy settings fo ...
  • a = [1,2,3,4,1,1,1,1] 刪除操作 刪除元素1 a.remove(1) 刪除第二個元素 del a[1] 預設刪除最後一個,給腳標就會刪除指定腳標元素 pop() 方法 a.pop() 搜索列表中的元素 a.index("sober") 統計元素出現次數 a.count(1) 添加 ...
  • 可以將二維數組理解為數組中的數組, 即一維數組裡在存一個一維數組 格式1:int[][] arr = new int[2][3]; arr的二維數組中有兩個一維數組,每個一維數組的大小是2。 arr[0]表示第一個一維數組,arr[0][0]表示第一個一維數組的第一個下標值。 格式2:int[][] ...
  • Eclipse導入web項目後,將web項目載入到server進行發佈時,提示Tomcat version 7.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 and 6 Web錯誤。 原因: 建立項目的時候會選擇所使用的J2EE版本,如果你選擇的 ...
  • Lambda 表達式是 C++11 中最重要的新特性之一。類似匿名函數,當需要一個函數但是又不想費力去命名時使用。這樣的場景其實有很多,所以匿名函數幾乎是現代編程語言的標配。 Lambda 表達式基礎 Lambda 表達式的基本語法如下: [捕獲列表](參數列表) mutable(可選)異常屬性 -... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...