【Python】【面向对象】类的特殊成员



2017年10月10日    Author:Guofei

文章归类: 0xd0_设计模式    文章编号: 1003

版权声明:本文作者是郭飞。转载随意,但需要标明原文链接,并通知本人
原文链接:https://www.guofei.site/2017/10/10/specialfield.html


1. 构造和析构

__init__

构造方法,通过类创建对象时,自动触发执行。
必须返回None(不写return)

__init__ 不是构造对象第一个执行的方法,__new__ 才是

__del__

析构方法,当对象在内存中被释放时,自动触发执行。

注:

  1. 析构函数本身不做资源回收,这是 Python 解释器的任务,析构函数是垃圾回收时同时触发执行的。
  2. 并不是说 del object 时都会执行,而是当引用数量为0,才会启用垃圾回收机制,并调用 __del__ 方法
  3. Foo() 没赋值,也没有给 _ ,因此引用变为0,触发 __del__
class Foo:
    def __del__(self):
        print('wtf?')
        pass # 虽然这里没写如何del,但仍然从内存中删除

这么用:

a = Foo()
b = a
del b # 不会触发 __del__ 因为引用数量不为0
del a # 触发 __del__,输出 "wtf?",虽然 __del__ 方法没有指明如何删除,但仍然会从内存中删除

__call__

__call__方法的执行是由对象后加括号触发的
obj() 或者 ClassNmae()()

2. 文档相关

__doc__

表示类的描述信息

class Foo:
    """ 描述类信息,会被 __doc__ 打印出来 """
    def func(self):
        pass

print(Foo.__doc__)  # 输出: 描述类信息,会被 __doc__ 打印出来

__str____repr__

class Foo:
    def __repr__(self):
        return 'repr method'


obj = Foo()
print(obj)
obj
# 输出:repr method

# __str__ 只重构与print(obj), str(obj) 相关的,会覆盖__repr__中的print功能
# __repr__ 既重构与print相关的,也重构与直接输出相关的

__format__, __bytes__

3. 属性访问

__getattr__(self,name) # 定义用户试图获取一个不存在的属性时的行为
__getattribute__(self,name) # 定义该类的属性被访问时的行为
__setattr__(self,name,value) # 定义当一个属性被设置时的行为
__delattr__(self,name)  # 定义当一个属性被删除时的行为

注意,覆写后要使用super()类来执行原本操作,否则会陷入无限递归

__dir__ # 用户 使用 dir(obj) 的行为,它返回 Iterable[str]

__get__, __set__, __delete__

4. 重载运算符号

二元运算符

方法 解释
__add__(self,other) 定义加法+
__sub__(self,other) 定义减法-
__mul__(self,other) 定义乘法*
__truediv__(self,other) 定义除法/
__floordiv__(self,other) 定义整除//
__mod__(self,other) 定义取模%
__divmode__(self,other) 定义divmode
__pow__(self,other) 定义乘方power**
__lshift__(self,other) 定义按位左移<<
__rshift__(self,other) 定义按位右移>>
__and__(self,other) 定义按位与&
__or__(self,other) 定义按位或|
__xor__(self,other) 定义按位异或^
class New_int(int):
    def __add__(self, other):
        return int.__sub__(self,other) # 别用 self +/- other , 否则无限递归
    def __sub__(self, other):
        return int.__add__(self,other)


a=New_int(5)
a+3

比较运算符:

方法 解释
__eq__(self, other) ==
__ne__ !=
__lt__ <
__le__ <=
__gt__ >
__ge__ >=

反运算

被操作的对象放到后面,还有一堆重载方法:

# 左操作数不支持相应操作时被调用,例如 1+<New_int> 这种写法
__radd__, __rsub__ , __rmul__ , __rturediv__ ,
__rfloordiv__ , __rmod__ , __rdivmod__ , __rpow__ ,
__rlshift__ , __rrshift__ , __rxor__ , __ror__

增量运算

方法 解释
__iadd__(self,other) 定义加法+=
__isub__(self,other) 定义减法-=
__imul__(self,other) 定义乘法*=
__itruediv__(self,other) 定义除法/=
__ifloordiv__(self,other) 定义整除//=
__imod__(self,other) 定义取模%=
__ipow__(self,other) 定义乘方power**=
__ilshift__(self,other) 定义按位左移<<=
__irshift__(self,other) 定义按位右移>>=
__iand__(self,other) 定义按位与&
__ior__(self,other) 定义按位或|
__ixor__(self,other) 定义按位异或^

一元操作符

方法 解释
__neg__(self) 定义负数-x
__pos__(self) 定义正数+x
__abs__(self) 定义绝对值abs()
__invert__(self) 定义求反~x

类型转换

方法 解释
__complex__(self) 定义complex()
__int__(self) 定义int()
__float__(self) 定义float()
__round__(self) 定义round()
__bool__(self) 定义 bool(obj),注意 __bool__ 必须返回 bool 类型

__hash__, __index__

5. 容器类型

方法 解释
__len__ 定义 len()
__getitem__ 定义 obj['key1'] ,同时定义 obj[1:9] 切片,同时定义 for i in obj(输入值其实是 0,1,… )
__setitem__ 定义 obj['key1'] = 'value1'
__delitem__ 定义 del obj['key1']
__contains__ 定义 1 in obj
__reversed__ 触发 reversed(obj),约定它返回一个迭代器
__iter__ 触发 iter(obj)
__next__ 触发 next(obj). 另外 for i in obj 会先调用 iter,然后调用next
class Foo(object):
    def __init__(self):
        self.n = 0

    def __getitem__(self, key):
        # 执行类似 obj[key] 的操作
        print('__getitem__', key)

    def __setitem__(self, key, value):
        # 执行类似 obj[key]=value 的操作
        print('__setitem__', key, value)

    def __delitem__(self, key):
        # 执行类似 del obj[key] 的操作
        print('__delitem__', key)

    def __len__(self):
        # 执行类似 len(obj) 的操作
        print('__len__')
        return 1  # 必须返回一个 integer ,否则报错

    def __contains__(self, item):
        # item in obj 和 item not in obj 触发
        print('__contains__')
        return 1  # 最后的 return 会被强制变成 bool 类型

    def __iter__(self):
        # 执行迭代器操作
        print('__iter__')
        return self

    def __next__(self):
        print('__next__')
        self.n += 1
        if self.n > 5:
            raise StopIteration # 用来结束 for 循环,或next
        return self.n

    def __reversed__(self):
        # reversed() 触发,约定它返回一个迭代器
        print('__reversed__')


obj = Foo()

obj['k1']              # 触发执行 __getitem__
obj['k2'] = 'wupeiqi'  # 触发执行 __setitem__
del obj['k1']          # 触发执行 __delitem__
len(obj)               # 触发执行 __len__
1 in obj               # 触发执行 __contains__
reversed(obj)          # 触发执行 __reversed__


for i in obj:
    print(i)

附:迭代器的用法

it=iter([1,2,3])
next(it)
next(it)
next(it)
next(it) # 迭代到头会报一个 StopIteration 错误

例2

class Fibs:
    def __init__(self):
        self.a = 0
        self.b = 1

    def __iter__(self):
        return self

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 100: raise StopIteration
        return self.a


fibs = Fibs()

for i in fibs:
    print(i)

7. 生成器

生成器的知识与类没太大关系,与迭代器一样强大,因此放这里。
利用生成器,可以实现 协同程序
所谓协同程序就是可以运行的独立函数调用,函数可以暂停或者挂起,并在需要的时候从程序离开的地方继续或者重新开始。

def myGen():
    print('生成器被执行!')
    yield 1
    yield 2

使用方法1

myG = myGen()
next(myG)
next(myG)
next(myG)  # 迭代到头后,会报一个 StopIteration 错误(同iter)

使用方法2

myG = myGen()
for i in myG:
    print(i)

案例:斐波那契数列

def fibs():
    a, b = 0, 1
    while b < 100:
        yield b
        a, b = b, a + b


for i in fibs():
    print(i)

[i for i in fibs()]

另一种生成器

gen = (x ** 2 for x in (1, 2, 3))

8. 上下文管理器with

__enter____exit__ 方法,具体看下面

使用上下文管理器有三个好处:

  • 提高代码的复用率;
  • 提高代码的优雅度;
  • 提高代码的可读性;

示例:

class Resource():
    def __init__(self, filename=''):
        print('初始化 context manager, filename = {}'.format(filename))
        self.filename = filename

    def __enter__(self):
        print('获取资源, filename = {}'.format(self.filename))
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(f'关闭资源,异常类型 = {exc_type}, 异常值 = {exc_val}, 异常的错误栈信息 = {exc_tb},如果主代码没有异常,三个参数都是 None')
        # return True,就相当于告诉Python解释器,主逻辑的异常我们已经捕获了,不需要再往外抛了
        # 如果 主逻辑本身就没有抛出异常,无论return True/False,都不会抛出异常
        return True

    def operate(self, num):
        1 / num  # 用来看 __exit__ 如何捕获一个错误
        print('执行某个操作,就是一个普通的方法')


# 可以同时 with 多个
with Resource('file1') as res1, Resource('file2') as res2:
    res1.operate(1)
    res2.operate(0)

运行结果:

初始化 context manager, filename = file1
获取资源, filename = file1
初始化 context manager, filename = file2
获取资源, filename = file2
执行某个操作,就是一个普通的方法
关闭资源,异常类型 = <class 'ZeroDivisionError'>, 异常值 = division by zero, 异常的错误栈信息 = <traceback object at 0x7fe433a88f00>,如果主代码没有异常,三个参数都是 None
关闭资源,异常类型 = None, 异常值 = None, 异常的错误栈信息 = None,如果主代码没有异常,三个参数都是 None

上下文管理器的优点:处理异常时,通常使用 try...except...,这会造成主代码中有大量的异常处理,很大影响可读性。

另外,上下文解释器也可以不是类,可以是函数,见于 文档

其它

__dict__

返回所有属性组成的dict
类或对象中的所有成员

  • 类:静态字段,方法(公有的、私有的)
  • 对象: 普通字段,普通私有字段格式如 _MyClass__PrivateDynamic。不包含方法,不包含静态字段(因为它们属于Class)

__module____class__

__module__表示当前操作的对象在那个模块

__class__表示当前操作的对象的类是什么

func.__code__


func.__code__.co_name  # 函数名
func.__code__.co_argcount  # 函数的参数个数,不包含*args与**kwargs
func.__code__.co_varnames  # tuple 形式的入参名,包含args,kwargs. ?有闭包时,还包含被闭包的函数名

func.__code__.co_filename  # 函数所在的文件
func.__code__.co_firstlineno  # 函数第一行在文件中的行数

func.__code__.co_kwonlyargcount  # 函数强制关键字参数个数

func.__code__.co_cellvars  # 闭包专用,外部函数中被内嵌函数调用的参数
func.__code__.co_freevars  # 闭包专用,内部函数中引用外部函数参数

# 下面这些还没查:
func.__code__.co_code  # ?似乎是编译后的二进制
func.__code__.co_consts  # ?似乎是涉及到的变量

func.__code__.co_flags
func.__code__.co_lnotab

func.__code__.co_names  # ??
func.__code__.co_nlocals
func.__code__.co_stacksize

另外

func.__defaults__ # 入参的默认值

与包相关

# filename.py

print(__name__) # __main__ 或者包名
print(__file__) # 文件所在绝对路径
print(__doc__) # 文件的注释

以上几个也可以作为调包时使用,例如:

import filename

filename.__name__
filename.__file__
filename.__doc__

参考文献:
http://python.jobbole.com/82023/

http://python.jobbole.com/83747/


您的支持将鼓励我继续创作!