Python面向对象进阶
类的特数成员方法
1.__doc__:类的描述信息:
class Foo:
"""描述类信息,滴答滴答滴啊..."""
pass
f = Foo()
print("这是类的描述信息:", f.__doc__) # 这是类的描述信息: 描述类信息,滴答滴答滴啊..
print("这是类的描述信息:", Foo.__doc__) # 这是类的描述信息: 描述类信息,滴答滴答滴啊...
2.__module__:当前操作对象所在模块:
class Foo:
"""描述类信息,滴答滴答滴啊..."""
pass
print("当前对象的模块:", Foo.__module__) # 当前对象的模块: __main__
f = Foo()
print("当前对象的模块:", f.__module__) # 当前对象的模块: __main__
3.__class__:当前操作对象的类:
class Foo:
"""描述类信息,滴答滴答滴啊..."""
pass
print("当前对象的类:", Foo.__class__) # 当前对象的类: <class 'type'>
f = Foo()
print("当前对象的类:", f.__class__) # 当前对象的类: <class '__main__.Foo'>
4.__init__:构造方法,通过类创建实例对象时,自动触发执行;
5.__del__:析构方法,当对象在内存中被释放时,自动触发执行;
此方法一般无需定义,因为Python是一门高级语言,程序员在使用时无需关系内存的分配和释放,因为此工作都是Python解释器来完成的,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。
class Foo:
def __del__(self):
print("执行删除!")
f = Foo()
print("*"*10) # **********
del f # 执行删除!
print("#"*10) # ##########
6.__call__:对象后面加括号(),触发执行;
构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象后加括号()触发的,即对象()或者类()()。
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print("() is execute %s ." % "__call__")
obj = Foo() # 执行__init__
obj() # 执行__call__:() is execute __call__ .
7.__dict__:查看类或者对象中的所有成员;
class Province:
country = "China"
def __init__(self, name, count):
self.name = name
self.count = count
def func(self, *args, **kwargs):
print("func")
# 获取类的成员,即:静态属性、方法
print(Province.__dict__) # {'__module__': '__main__', 'country': 'China', '__init__': <function Province.__init__ at 0x000001B4CACC9950>, 'func': <function Province.func at 0x000001B4CACC99D8>, '__dict__': <attribute '__dict__' of 'Province' objects>, '__weakref__': <attribute '__weakref__' of 'Province' objects>, '__doc__': None}
shanghai = Province("Shang Hai", 12000000)
# 获取对象shanghai的成员
print(shanghai.__dict__) # {'name': 'Shang Hai', 'count': 12000000}
hongkong = Province("Hong Kong", 240000000)
# 获取对象hongkong的成员
print(hongkong.__dict__) # {'name': 'Hong Kong', 'count': 240000000}
8.__str__、__repr__和__format__:__str__、__repr__,改变对象的字符串显示;__format__,自定义格式化字符串;
- __str__、__repr__
>>> class Student:
... def __init__(self, name):
... self.name = name
...
>>> print(Student("Jack Ma"))
<__main__.Student object at 0x7fedeb012e80>
打印出<__main__.Student object at 0x7fedeb012e80>,不明觉厉。那么怎么才能打印出我们好读的内容呢?秩序定义好__str__()方法,返回我们想要的内容即可:
>>> class Student:
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return "Student object name: %s" % self.name
...
>>> print(Student("Jack"))
Student object name: Jack
这样打印出来的实例,不但好看,而且容易看出实例内部的重要数据。但是,细心的同学会发现直接敲变量不用print()函数,打印出来的实例还是不好看:
>>> s = Student("Pony")
>>> s
<__main__.Student object at 0x7fedeb0197f0>
这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,repr()是为调试服务的。
解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的,所以,有个偷懒的写法:
>>> class Student:
... def __init__(self, name):
... self.name = name
... def __str__(self):
... return "Student object name: %s" % self.name
... __repr__ = __str__
...
>>> s = Student("LiYanhong")
>>> s
Student object name: LiYanhong
- __format__:自定义格式化字符串:
date_dict = {
"ymd": "{0.year}:{0.month}:{0.day}",
"dmy": "{0.day}/{0.month}/{0.year}",
"mdy": "{0.month}-{0.day}-{0.year}",
}
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __format__(self, format_spec):
if not format_spec or format_spec not in date_dict:
format_spec = "ymd"
fmt = date_dict[format_spec]
return fmt.format(self)
d = Date(2018, 5, 14)
print(format(d)) # 2018:5:14
print("{:mdy}".format(d)) # 5-14-2018
9.__getitem__、__setitem__和__delitem__:用于类似字典的操作,分别表示获取、设置和删除数据:
class Foo:
def __getitem__(self, item):
print("__getitem__", item)
def __setitem__(self, key, value):
print("__setitem__", key, value)
def __delitem__(self, key):
print("__delitem__", key)
f = Foo()
res = f["Jack"] # 自动触发执行__getitem__:__getitem__ Jack
f["Rose"] = "Marry" # 自动触发执行__setitem__:__setitem__ Rose Marry
del f["Jack"] # 自动触发执行__delitem__:__delitem__ Jack
10.__getattr__、__setattr__和__delattr__
-
__getattr__
拦截点号运算,当对未定义的属性名和实例进行点号运算时,就会用属性名作为字符串调用这个方法,如果继承树可以找到该属性,则不调用此方法。 -
__setattr__
拦截所有属性赋值语句,如果定义了这个方法,self.attr = value就会变成self.setattr("attr", value)。这个需要特别注意,当在__setattr__方法内对属性进行赋值时,不可使用self.attr=value,因为它会再次调用self.setattr("attr", value),则会形成无限递归循环,最后导致堆栈溢出异常。应该通过对属性字典做索引运算来赋值任何实例属性,也就是使用self.dict["attr"]=value。 -
__delattr__
删除属性时触发执行;
class Foo:
static_var = 10
def __init__(self, var):
self.var = var
def __getattr__(self, item):
print("from __getattr__:你找的属性不存在!")
def __setattr__(self, key, value):
print("from __setattr__")
# self.key = value # 这就无限递归了
# self.__dict__[key] = value # 应该使用它
def __delattr__(self, item):
print("from __delattr__")
# del self.item # 无限递归了
self.__dict__.pop(item)
# __setattr__:添加/修改属性都会触发它的执行
f = Foo(20)
print(f.__dict__) # 因为这里重写了__setattr__,凡是赋值操作都会触发它的执行,这里什么都没写,也就没有赋值,除非直接操作字典,否则永远无法赋值
f.temp = 30
print(f.__dict__)
f.__dict__["x"] = 40 # 我们可以直接修改属性字典,来完成添加/修改属性的操作
print(f.__dict__)
# __delattr__:删除属性的时候触发执行
del f.x
print(f.__dict__)
# __getattr__:只有在使用点调用属性且不存在时才会触发
f.variable
运行输出结果为:
from __setattr__
{}
from __setattr__
{}
{'x': 40}
from __delattr__
{}
from __getattr__:你找的属性不存在!
isinstance和issubclass
1.isinstance(obj, cls):检查对象obj是否是类cls的对象:
>>> class Foo:
... pass
...
>>> f = Foo()
>>> isinstance(f, Foo)
True
2.issubclass(sub, super):检查sub类是否是super类的派生类:
>>> class Foo:
... pass
...
>>>
>>> class Bar(Foo):
... pass
...
>>> issubclass(Bar, Foo)
True
反射
1.反射的概念
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。
2.反射的实现
Python面向对象中的反射:通过字符串的形式操作对象相关的属性,Python中的一切事物都是对象,都可以使用反射。
Python中4个可以实现自省的函数,下列方法适用于类和对象(一切皆对象,类本身也是一个对象)。
- hasattr(object, name)
判断一个对象里面是否有name属性或name方法,返回bool值,有name属性或方法返回true,否则返回false。需要注意的是name要用括号()括起来:
>>> class Test:
... name = "USA"
... def run(self):
... return "Hello World!"
...
>>> t = Test()
>>> hasattr(t, "name")
True
>>> hasattr(t, "run")
True
>>> hasattr(t, "everything")
False
- getattr(object, name, default=None)
获取对象object的属性或方法,如果存在打印出来,如果不存在打印出默认值,默认值可选。需要注意的是,如果获取的对象方法,返回的是方法的内存地址,如果需要运行这个方法,可以在后面添加括号()执行:
>>> class Test:
... name = "Jack"
... def run(self):
... return "Hello World!"
...
>>> t = Test()
>>> getattr(t, "name") # 获取name属性,存在就打印出来
'Jack'
>>> getattr(t, "run") # 获取run方法,存在就打印出方法的内存地址
<bound method Test.run of <__main__.Test object at 0x7f0d66cbee80>>
>>> getattr(t, "run")() # 获取run方法,后面加括号()可以执行该方法
'Hello World!'
>>> getattr(t, "age") # 获取一个不存在的属性
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute 'age'
>>> getattr(t, "age", "18") # 若属性不存在,返回默认值
'18'
- setattr(object, name, values)
给对象的属性赋值,若属性不存在,先创建再赋值:
>>> class Test:
... name = "Jack"
... def run(self):
... return "Hello World!"
...
>>> t = Test()
>>> hasattr(t, "age")
False
>>> setattr(t, "age", "22")
>>> hasattr(t, "age")
True
>>> getattr(t, "age")
'22'
- delattr(object, name)
删除object对象名为name的属性,如下综合例子:
class Intermediary:
feature = "white"
def __init__(self, name, addr):
self.name = name
self.addr = addr
def sell_house(self):
print("%s 卖房子" % self.name)
def rent_house(self):
print("%s 租房子" % self.name)
intermediary = Intermediary("Wanke", "高新区")
# 检测是否含有某属性
print(hasattr(intermediary, "name"))
print(hasattr(intermediary, "sell_house"))
# 获取属性
name = getattr(intermediary, "name")
func = getattr(intermediary, "rent_house")
# 获取不存在的属性,设置默认值
print(getattr(intermediary, "something", "不存在的属性!"))
# 设置属性
setattr(intermediary, "flag", True)
setattr(intermediary, "show_name", lambda self: self.name + " suffix")
print(intermediary.__dict__)
print(intermediary.show_name(intermediary))
# 删除属性
delattr(intermediary, "addr")
delattr(intermediary, "show_name")
# delattr(intermediary, "gender") # 删除不存在的属性,报错
print(intermediary.__dict__)
运行输出结果为:
True
True
不存在的属性!
{'name': 'Wanke', 'addr': '高新区', 'flag': True, 'show_name': <function <lambda> at 0x000002326DA11EA0>}
Wanke suffix
{'name': 'Wanke', 'flag': True}
万物皆对象,类也是对象
class Foo:
static_field = "static"
def __init__(self, name):
self.name = name
def func(self):
return "func"
@staticmethod
def bar():
return "bar"
print(getattr(Foo, "static_field")) # static
print(getattr(Foo, "func")) # <function Foo.func at 0x000001BB676D99D8>
print(getattr(Foo, "bar")) # <function Foo.bar at 0x000001BB676D9A60>
3.反射的好处
- 实现可拔插机制
可以预先定义好接口,接口只有在被调用时才会真正执行,这实现了即插即用,这其实是一种"后期绑定",即可以先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能:
# 程序员A未实现功能
class FtpClient:
"""ftp客户端,但是还未实现具体功能"""
def __init__(self, addr):
print("正在连接服务器[%s]" % addr)
self.addr = addr
但不影响程序员B继续实现其他逻辑,利用反射事先做判断
# from module import FtpClient
f = FtpClient("192.168.1.100")
if hasattr(f, "get"): # 判断方法是否实现
func_get = getattr(f, "get")
func_get()
else:
print("不存在此方法!")
print("处理其他的逻辑!")
- 动态导入模块
动态导入模块方法:1.import;2.import importlib
说明:
1.函数功能用于动态的导入模块,主要用于反射或者延迟加载模块;
2.import(module)相当于import module
方法1实例:首先,创建目录language;然后在该目录下创建模块:lang.py,其代码为:
class Foo:
def __str__(self):
return "Python language"
在language目录的平级目录创建测试模块:test.py,使用__import__动态以字符串形式导入language下的lang模块:
l = __import__("language.lang") # 相当于import language
python = l.lang.Foo()
print(python)
方法2实例:使用importlib进行动态导入(此方法好理解,也是官方推荐使用)
import importlib
la = importlib.import_module("language.lang")
res = la.Foo()
print(res)
实例如下截图:
元类
1.元类的定义
元类是用来控制如何创建类的,正如类时创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。元类的实例化结果为我们用class定义的类,正如类的实例为对象(f对象是Foo类的一个实例,Foo类是type类的一个实例)。type是Python的一个内建元类,用来直接控制类生成,Python中任何class定义的类其实都是type类实例化的对象。
2.创建类的方式
- 使用class关键字
class Chinese:
country = "China"
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print("%s is talking..." % self.name)
- 手动模拟class创建类的过程:将创建类的步骤拆开,手动创建
创建类主要分为三部分:
1.类名;2.类的父类;3.类体;
# 类名
class_name = "Chinese"
# 类的父类
class_bases = (object,)
# 类体
class_body = """
country = "China"
def __init__(self, name, age):
self.name = name
self.age = age
def talk(self):
print("%s is talking..." % self.name)
"""
步骤1(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的命名空间),我们可以预先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典
class_dict = {}
exec(class_body, globals(), class_dict)
print(class_dict)
# {'country': 'China', '__init__': <function __init__ at 0x000001EDE6851EA0>, 'talk': <function talk at 0x000001EDE8529950>}
步骤2:调用元类type(也可以自定义)来产生Chinese
Foo = type(class_name, class_bases, class_dict) # 实例化type得到对象Foo,即我们用class定义的Foo
print(Foo) # <class '__main__.Chinese'>
print(type(Foo)) # <class 'type'>
print(isinstance(Foo, type)) # True
我们看到,type接受三个参数:
- 1.第1个参数是字符串"Foo",表示类名;
- 2.第2个参数是元组(object),表示所有的父类;
- 3.第3个参数是字典,这里是一个空字典,表示没有定义属性和方法;
补充:若Foo类有继承,即class Foo(Bar):...,则等同于type("Foo", (Bar,), {});一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类;
自定义元类精简版
class MyType(type):
def __init__(self, what, bases=None, dict=None):
print(what, bases, dict)
def __call__(self, *args, **kwargs):
print("*"*12)
obj = object.__new__(self)
self.__init__(obj, *args, **kwargs)
return obj
class Room(metaclass=MyType):
def __init__(self, name):
self.name = name
room = Room("Province")
print(room.__dict__)
运行输出结果为:
Room () {'__module__': '__main__', '__qualname__': 'Room', '__init__': <function Room.__init__ at 0x000002007B099A60>}
************
{'name': 'Province'}