多继承
狮虎兽, 不知道你有没有听说过? 狮虎兽, 是雄狮 (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
property 的使用 http://www.runoob.com/python/python-func-property.html
魔法属性 / 魔法方法 https://docs.python.org/3/reference/datamodel.html#special-method-names
(译)Python 魔法方法指南 http://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html
来源: http://www.jianshu.com/p/c043efbb9d8c