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

字数 1542阅读 185

多继承

狮虎兽, 不知道你有没有听说过? 狮虎兽,是雄狮(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魔法方法指南

推荐阅读更多精彩内容