Python学习笔记二十二(多继承 / 属性和方法 / 常量 / with和上下文管理器 )

多继承

狮虎兽, 不知道你有没有听说过? 狮虎兽,是雄狮(Panthera leo)与雌虎(Panthera tigris)杂交后的产物,是属于猫科豹属的一员.
用程序模拟一下狮虎兽.

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__)  # 类初始化的顺序表

# 运行结果:
# init LionTiger
# init Lion
# init Animal
# init Tiger
# init Animal
# (<class '__main__.LionTiger'>, <class '__main__.Lion'>, <class '__main__.Tiger'>, <class '__main__.Animal'>, <class 'object'>)

当子类有多个父类 ( Lion 和 Tiger ), 并且父类又有一个共同的父类 ( Animal ) 时, 使用父类名. 父类方法 ( Lion.__init__(self) ) 强制调用的方式父类的父类 ( Animal ) 会被多次初始化. 怎么解决这个问题? 我们发现如果按照LionTiger.__mro__ 属性值的顺序去初始化, 父类的父类 ( Animal ) 只会被初始化一次. 那么怎么使类按照这个顺序取初始化呢?

super() 调用
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__)  # 类初始化的顺序表

# 运行结果:
# init LionTiger
# init Lion
# init Tiger
# init Animal
# (<class '__main__.LionTiger'>, <class '__main__.Lion'>, <class '__main__.Tiger'>, <class '__main__.Animal'>, <class 'object'>)

通过super() 调用父类可以按照__mro__的顺序初始化父类

方法

实例方法
class Test(object):
    def func(self):
        print("func")


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

# 运行结果
# func
# func
# ******************************
# {'__module__': '__main__', 'func': <function Test.func at 0x0000022CF9BE89D8>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

通过运行结果以及__dict__属性可以看出 类对象以及 实例对象都可以调用实例方法, 但是不建议使用类对象调用实例方法. 同时 类对象的__dict__ 有 'func': <function Test.func at 0x0000022CF9BE89D8> , 而 实例对象的__dict__ 为 {} , 所以可以得出 实例方法是存储在类对象的内存中

类方法
class Test(object):

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


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

# 运行结果
# func
# func
# ******************************
# {'__module__': '__main__', 'func': <classmethod object at 0x000001BABFD66940>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

通过运行结果以及__dict__属性可以看出 类对象以及 实例对象都可以调用类方法, 但是不建议使用 实例对象调用 类方法. 同时 类对象的__dict__ 有 'func': <function Test.func at 0x000001BABFD66940> , 而 实例对象的__dict__ 为 {} , 所以可以得出 类方法是存储在类对象的内存中

静态方法
class Test(object):
    @staticmethod
    def func():
        print("func")


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

# 运行结果
# func
# func
# ******************************
# {'__module__': '__main__', 'func': <staticmethod object at 0x00000200193E6940>, '__dict__': <attribute '__dict__' of 'Test' objects>, '__weakref__': <attribute '__weakref__' of 'Test' objects>, '__doc__': None}
# {}

通过运行结果以及__dict__属性可以看出 类对象以及 实例对象都可以调用静态方法, 但是不建议使用 实例对象调用 静态方法. 同时 类对象的__dict__ 有 'func': <function Test.func at 0x000001BABFD66940> , 而 实例对象的__dict__ 为 {} , 所以可以得出 静态方法是存储在类对象的内存中

私有方法
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__)

# 运行结果
# 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}
# {}

私有方法不能在类外访问 ( 正常来说 ), 通过 类对象以及 实例对象都可以在类内访问私有方法. 同时 类对象的__dict__ 有 'func': <function Test.func at 0x000001BABFD66940> , 而 实例对象的__dict__ 为 {} , 所以可以得出 私有方法是存储在类对象的内存中

属性

那么类属性以及实例属性存储在哪里?

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__)

# 运行结果
# 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'}

通过运行结果以及__dict__属性可以看出 类对象以及 实例对象都可以调用类属性, 但是不建议使用 实例对象调用类属性. 同时 类对象的__dict__ 有'class_property': 'class_property' 说明类属性存储在类对象的内存中, 实例对象的__dict__ 有 'property': 'property' 说明实例属性存储在实例对象的内存中

小结:

  • 公有方法:实例方法 / 类方法 / 静态方法, 类对象和实例对象在类内类外都可以调用.
  • 私有方法: 类对象和实例对象在类内都可以调用.
  • 方法: 公有方法 / 私有方法 , 都存储在类对象的内存中.
  • 类属性: 类对象和实例对象在类内类外都可以调用, 类属性存储在类对象的内存中.
  • 实例属性: 类对象不能调用, 实例对象在类内可以调用, 实例属性存储在实例对象的内存中.
  • 私有类属性: 类对象和实例对象在类内都可以调用, 私有类属性存储在类对象的内存中.
  • 私有实例属性:类对象不能调用, 实例对象在类内可以调用, 私有实例属性存储在实例对象的内存中.

常量

其它语言中有常量,比如 Java中 使用static final 定义一个静态常量, Python中的常量怎么定义?

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

    def get_property(self):
        return self.__property


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

# 运行结果
# property

通过私有属性构造 Python中的常量, 和其它语言中的不一样?再来

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


test = Test()
print(test.get_property)

# 运行结果
# property

这样是不是有点感觉了?再来

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

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


test = Test()
print(test.PI)

# 运行结果
# 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)

# 运行结果
# 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__ 被调用"

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


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

# 运行结果:
# cls.instance = __str__ 被调用
# self.name = DragonFang
# cls.instance = __str__ 被调用
# self.name = DragonFang
# [__str__ 被调用] 被删除

通过Python 单例模式观察 __new__(cls[, ...]) / __init__(self[, ...]) / __str__(self) / __del__(self) 魔法方法的调用过程以及作用

  • __new__(cls[, ...]) : 实例对象创建时被调用,可以控制实例对象的创建
  • __init__(self[, ...]) : 实例对象创建后,初始化时被调用,可以控制实例对象的初始化
  • __str__(self) : 打印对象时被调用, 可以返回对象的描述信息
  • __del__(self) : 对象被销毁时调用, 可以做一些关闭操作
__module__ 和 __class__
import threading

my_thread = threading.Thread()

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

# 运行结果
# threading  表示当前操作的对象来自哪一个模块
# <class 'threading.Thread'>  表示当前操作对象属于哪一个类
__dict__ 类或对象中的所有属性

上面已经使用过了这里不做赘述.
魔法属性或 方法就说到这里, 有兴趣的可以通过角注了解其它的魔法属性 或者方法.

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

这种操作文件的方式很简洁, 那么with 内部做了什么?讨论这个问题之前, 先要明白另一个概念上下文管理器

上下文管理器

上下文管理器, 对当前的环境进行一定的自动处理, 如文件的关闭操作.
作用: 使用上下文管理器可以避免一些重复的 / 琐碎的操作.
怎么自定义个上下文管理器? 包含 __enter__() 和 __exit__()方法的类,就是一个上下文管理器.

自定义上下文管理器

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

通过简单的骨架,可以找到一个上下文管理器的必备方法, 完善一下

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条数据" % rows)
    print("内容如下")
    for tmp in result_data:
        print(tmp)

# 运行结果:
# __init__
# __enter__
# 有10条数据
# 内容如下
# (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 后跟的是一个对象, 通过init 可以进行一些初始化操作 ( 比如连接数据库 / 得到cursor 对象) , 通过 as 得到 __enter__ 方法返回的对象, 进行一下操作 ( 比如查询数据库) , 执行结束自动调用__exit__方法, 可以将一些琐碎的操作放到方法体中 ( 比如关闭数据库连接)


到此结 DragonFangQy 2018.5.23


  1. property的使用

  2. 魔法属性 / 魔法方法

  3. (译)Python魔法方法指南

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容