Python學(xué)習(xí)筆記二十二(多繼承 / 屬性和方法 / 常量 / with和上下文管理器 )

多繼承

獅虎獸, 不知道你有沒有聽說過? 獅虎獸,是雄獅(Panthera leo)與雌虎(Panthera tigris)雜交后的產(chǎn)物,是屬于貓科豹屬的一員.
用程序模擬一下獅虎獸.

class Animal(object):
    def __init__(self):
        print("init Animal")


class Tiger(Animal):
    def __init__(self):
        print("init Tiger")
        Animal.__init__(self)


class Lion(Animal):
    def __init__(self):
        print("init Lion")
        Animal.__init__(Lion)


class LionTiger(Lion, Tiger):
    def __init__(self):
        print("init LionTiger")
        Lion.__init__(self)
        Tiger.__init__(self)


LionTiger()
print(LionTiger.__mro__)  # 類初始化的順序表

# 運(yùn)行結(jié)果:
# init LionTiger
# init Lion
# init Animal
# init Tiger
# init Animal
# (<class '__main__.LionTiger'>, <class '__main__.Lion'>, <class '__main__.Tiger'>, <class '__main__.Animal'>, <class 'object'>)

當(dāng)子類有多個(gè)父類 ( Lion 和 Tiger ), 并且父類又有一個(gè)共同的父類 ( Animal ) 時(shí), 使用父類名. 父類方法 ( Lion.__init__(self) ) 強(qiáng)制調(diào)用的方式父類的父類 ( Animal ) 會被多次初始化. 怎么解決這個(gè)問題? 我們發(fā)現(xiàn)如果按照LionTiger.__mro__ 屬性值的順序去初始化, 父類的父類 ( Animal ) 只會被初始化一次. 那么怎么使類按照這個(gè)順序取初始化呢?

super() 調(diào)用
class Animal(object):
    def __init__(self):
        print("init Animal")


class Tiger(Animal):
    def __init__(self):
        print("init Tiger")
        super().__init__()


class Lion(Animal):
    def __init__(self):
        print("init Lion")
        super().__init__()


class LionTiger(Lion, Tiger):
    def __init__(self):
        print("init LionTiger")
        super().__init__()


LionTiger()
print(LionTiger.__mro__)  # 類初始化的順序表

# 運(yùn)行結(jié)果:
# init LionTiger
# init Lion
# init Tiger
# init Animal
# (<class '__main__.LionTiger'>, <class '__main__.Lion'>, <class '__main__.Tiger'>, <class '__main__.Animal'>, <class 'object'>)

通過super() 調(diào)用父類可以按照__mro__的順序初始化父類

方法

實(shí)例方法
class Test(object):
    def func(self):
        print("func")


test = Test()
Test.func(test)
test.func()
print("*" * 30)
print(Test.__dict__)
print(test.__dict__)

# 運(yùn)行結(jié)果
# func
# func
# ******************************
# {'__module__': '__main__', 'func': <function Test.func at 0x0000022CF9BE89D8>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

通過運(yùn)行結(jié)果以及__dict__屬性可以看出 類對象以及 實(shí)例對象都可以調(diào)用實(shí)例方法, 但是不建議使用類對象調(diào)用實(shí)例方法. 同時(shí) 類對象的__dict__ 有 'func': <function Test.func at 0x0000022CF9BE89D8> , 而 實(shí)例對象的__dict__ 為 {} , 所以可以得出 實(shí)例方法是存儲在類對象的內(nèi)存中

類方法
class Test(object):

    @classmethod
    def func(cls):
        print("func")


test = Test()
Test.func()
test.func()
print("*" * 30)
print(Test.__dict__)
print(test.__dict__)

# 運(yùn)行結(jié)果
# func
# func
# ******************************
# {'__module__': '__main__', 'func': <classmethod object at 0x000001BABFD66940>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

通過運(yùn)行結(jié)果以及__dict__屬性可以看出 類對象以及 實(shí)例對象都可以調(diào)用類方法, 但是不建議使用 實(shí)例對象調(diào)用 類方法. 同時(shí) 類對象的__dict__ 有 'func': <function Test.func at 0x000001BABFD66940> , 而 實(shí)例對象的__dict__ 為 {} , 所以可以得出 類方法是存儲在類對象的內(nèi)存中

靜態(tài)方法
class Test(object):
    @staticmethod
    def func():
        print("func")


test = Test()
Test.func()
test.func()
print("*" * 30)
print(Test.__dict__)
print(test.__dict__)

# 運(yùn)行結(jié)果
# func
# func
# ******************************
# {'__module__': '__main__', 'func': <staticmethod object at 0x00000200193E6940>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

通過運(yùn)行結(jié)果以及__dict__屬性可以看出 類對象以及 實(shí)例對象都可以調(diào)用靜態(tài)方法, 但是不建議使用 實(shí)例對象調(diào)用 靜態(tài)方法. 同時(shí) 類對象的__dict__ 有 'func': <function Test.func at 0x000001BABFD66940> , 而 實(shí)例對象的__dict__ 為 {} , 所以可以得出 靜態(tài)方法是存儲在類對象的內(nèi)存中

私有方法
class Test(object):
    def __func(self):
        print("func")
    def test(self):
        self.__func()
        Test.__func(self)


test = Test()
test.test()
print("*" * 30)
print(Test.__dict__)
print(test.__dict__)

# 運(yùn)行結(jié)果
# func
# func
# ******************************
# {'__module__': '__main__', '_Test__func': <function Test.__func at 0x00000240FEC989D8>, 'test': <function Test.test at 0x00000240FEC98A60>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

私有方法不能在類外訪問 ( 正常來說 ), 通過 類對象以及 實(shí)例對象都可以在類內(nèi)訪問私有方法. 同時(shí) 類對象的__dict__ 有 'func': <function Test.func at 0x000001BABFD66940> , 而 實(shí)例對象的__dict__ 為 {} , 所以可以得出 私有方法是存儲在類對象的內(nèi)存中

屬性

那么類屬性以及實(shí)例屬性存儲在哪里?

class Test(object):
    class_property = "class_property"

    def __init__(self):
        self.property = "property"


test = Test()
print(Test.class_property)
print(test.class_property)

print("*" * 30)
# print(Test.property)
print(test.property)

print("*" * 30)
print(Test.__dict__)
print(test.__dict__)

# 運(yùn)行結(jié)果
# class_property
# class_property
# ******************************
# property
# ******************************
# {'__module__': '__main__', 'class_property': 'class_property', '__init__': <function Test.__init__ at 0x00000286E96A8A60>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {'property': 'property'}

通過運(yùn)行結(jié)果以及__dict__屬性可以看出 類對象以及 實(shí)例對象都可以調(diào)用類屬性, 但是不建議使用 實(shí)例對象調(diào)用類屬性. 同時(shí) 類對象的__dict__ 有'class_property': 'class_property' 說明類屬性存儲在類對象的內(nèi)存中, 實(shí)例對象的__dict__ 有 'property': 'property' 說明實(shí)例屬性存儲在實(shí)例對象的內(nèi)存中

小結(jié):

  • 公有方法:實(shí)例方法 / 類方法 / 靜態(tài)方法, 類對象和實(shí)例對象在類內(nèi)類外都可以調(diào)用.
  • 私有方法: 類對象和實(shí)例對象在類內(nèi)都可以調(diào)用.
  • 方法: 公有方法 / 私有方法 , 都存儲在類對象的內(nèi)存中.
  • 類屬性: 類對象和實(shí)例對象在類內(nèi)類外都可以調(diào)用, 類屬性存儲在類對象的內(nèi)存中.
  • 實(shí)例屬性: 類對象不能調(diào)用, 實(shí)例對象在類內(nèi)可以調(diào)用, 實(shí)例屬性存儲在實(shí)例對象的內(nèi)存中.
  • 私有類屬性: 類對象和實(shí)例對象在類內(nèi)都可以調(diào)用, 私有類屬性存儲在類對象的內(nèi)存中.
  • 私有實(shí)例屬性:類對象不能調(diào)用, 實(shí)例對象在類內(nèi)可以調(diào)用, 私有實(shí)例屬性存儲在實(shí)例對象的內(nèi)存中.

常量

其它語言中有常量,比如 Java中 使用static final 定義一個(gè)靜態(tài)常量, Python中的常量怎么定義?

class Test(object):
    def __init__(self):
        self.__property = "property"

    def get_property(self):
        return self.__property


test = Test()
print(test.get_property())

# 運(yùn)行結(jié)果
# property

通過私有屬性構(gòu)造 Python中的常量, 和其它語言中的不一樣?再來

class Test(object):
    def __init__(self):
        self.__property = "property"
    @property
    def get_property(self):
        return self.__property


test = Test()
print(test.get_property)

# 運(yùn)行結(jié)果
# property

這樣是不是有點(diǎn)感覺了?再來

class Test(object):
    def __init__(self):
        self.__PI = 3.1415926

    @property
    def PI(self):
        return self.__PI


test = Test()
print(test.PI)

# 運(yùn)行結(jié)果
# 3.1415926

是不是很有感覺了?那么property[1] 能干嘛?

class Test(object):
    def __init__(self):
        self.__PI = 3.1415926

    @property
    def PI(self):
        return self.__PI

    @PI.setter
    def PI(self, arg):
        self.__PI = arg

    @PI.deleter
    def PI(self):
        del self.__PI


test = Test()
print(test.PI)

test.PI = 3.14
print(test.PI)

del test.PI
# print(test.PI)

# 運(yùn)行結(jié)果
# 3.1415926
# 3.14

魔法屬性/魔法方法[2]/[3]

__new__(cls[, ...]) / __init__(self[, ...]) / __str__(self) / __del__(self)
class Animal(object):
    instance = None
    is_init = False

    def __new__(cls, *args, **kwargs):
        if not cls.instance:
            cls.instance = object().__new__(cls)
        print("cls.instance = %s" % cls.instance)
        return cls.instance

    def __init__(self, name):

        if not Animal.is_init:
            self.name = name
            Animal.is_init = True

        print("self.name = %s" % self.name)

    def __str__(self):
        return "__str__ 被調(diào)用"

    def __del__(self):
        print("[%s] 被刪除" % self)


animal = Animal("DragonFang")
animal2 = Animal("fang")

# 運(yùn)行結(jié)果:
# cls.instance = __str__ 被調(diào)用
# self.name = DragonFang
# cls.instance = __str__ 被調(diào)用
# self.name = DragonFang
# [__str__ 被調(diào)用] 被刪除

通過Python 單例模式觀察 __new__(cls[, ...]) / __init__(self[, ...]) / __str__(self) / __del__(self) 魔法方法的調(diào)用過程以及作用

  • __new__(cls[, ...]) : 實(shí)例對象創(chuàng)建時(shí)被調(diào)用,可以控制實(shí)例對象的創(chuàng)建
  • __init__(self[, ...]) : 實(shí)例對象創(chuàng)建后,初始化時(shí)被調(diào)用,可以控制實(shí)例對象的初始化
  • __str__(self) : 打印對象時(shí)被調(diào)用, 可以返回對象的描述信息
  • __del__(self) : 對象被銷毀時(shí)調(diào)用, 可以做一些關(guān)閉操作
__module__ 和 __class__
import threading

my_thread = threading.Thread()

print(my_thread.__module__)
print(my_thread.__class__)

# 運(yùn)行結(jié)果
# threading  表示當(dāng)前操作的對象來自哪一個(gè)模塊
# <class 'threading.Thread'>  表示當(dāng)前操作對象屬于哪一個(gè)類
__dict__ 類或?qū)ο笾械乃袑傩?/h5>

上面已經(jīng)使用過了這里不做贅述.
魔法屬性或 方法就說到這里, 有興趣的可以通過角注了解其它的魔法屬性 或者方法.

with
 with open("DragonFang.txt", "w") as f:
        f.write("DragonFang")

這種操作文件的方式很簡潔, 那么with 內(nèi)部做了什么?討論這個(gè)問題之前, 先要明白另一個(gè)概念上下文管理器

上下文管理器

上下文管理器, 對當(dāng)前的環(huán)境進(jìn)行一定的自動(dòng)處理, 如文件的關(guān)閉操作.
作用: 使用上下文管理器可以避免一些重復(fù)的 / 瑣碎的操作.
怎么自定義個(gè)上下文管理器? 包含 __enter__() 和 __exit__()方法的類,就是一個(gè)上下文管理器.

自定義上下文管理器

class OpenDB(object):
    def __init__(self):
        pass

    def __enter__(self):
        pass

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass


with OpenDB() as db:
    pass

通過簡單的骨架,可以找到一個(gè)上下文管理器的必備方法, 完善一下

from pymysql import connect


class OpenDB(object):
    def __init__(self, dbname):
        print("__init__")
        self.conn_sql = connect(host="localhost", port=3306, user="root", password="mysql", database=dbname,
                                charset="utf8")
        self.cursor_sql = self.conn_sql.cursor()

    def __enter__(self):

        print("__enter__")
        return self.cursor_sql

    def __exit__(self, exc_type, exc_val, exc_tb):

        print("__exit__")
        self.conn_sql.commit()
        self.cursor_sql.close()
        self.conn_sql.close()


with OpenDB("fang") as db:
    rows = db.execute("select * from emp;")
    result_data = db.fetchall()

    print("有%s條數(shù)據(jù)" % rows)
    print("內(nèi)容如下")
    for tmp in result_data:
        print(tmp)

# 運(yùn)行結(jié)果:
# __init__
# __enter__
# 有10條數(shù)據(jù)
# 內(nèi)容如下
# (0, 'name0', 'm')
# (1, 'name1', 'w')
# (2, 'name2', 'w')
# (3, 'name3', 'm')
# (4, 'name4', 'w')
# (5, 'name5', 'w')
# (6, 'name6', 'm')
# (7, 'name7', 'm')
# (8, 'name8', 'w')
# (9, 'name9', 'w')
# __exit__

通過自定義上下文管理器, 可以得知 with 后跟的是一個(gè)對象, 通過init 可以進(jìn)行一些初始化操作 ( 比如連接數(shù)據(jù)庫 / 得到cursor 對象) , 通過 as 得到 __enter__ 方法返回的對象, 進(jìn)行一下操作 ( 比如查詢數(shù)據(jù)庫) , 執(zhí)行結(jié)束自動(dòng)調(diào)用__exit__方法, 可以將一些瑣碎的操作放到方法體中 ( 比如關(guān)閉數(shù)據(jù)庫連接)


到此結(jié)?DragonFangQy 2018.5.23


  1. property的使用 ?

  2. 魔法屬性 / 魔法方法 ?

  3. (譯)Python魔法方法指南 ?

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

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

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