多继承
狮虎兽, 不知道你有没有听说过? 狮虎兽,是雄狮(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