6

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

 3 years ago
source link: https://www.guofei.site/2017/10/10/specialfield.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

2017年10月10日

Author: Guofei

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


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

Edit

1. 构造和析构

__init__">__init__

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

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

__del__">__del__

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

  1. 析构函数本身不做资源回收,这是 Python 解释器的任务,析构函数是垃圾回收时同时触发执行的。
  2. 并不是说 del object 时都会执行,而是当引用数量为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__

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

2. 文档相关

__doc__">__doc__

表示类的描述信息

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

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

__str__与__repr__">__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__

__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'] __setitem__ 定义 obj['key1'] = 'value1' __delitem__ 定义 del obj['key1'] __contains__ 定义 1 in obj

class Foo(object):

    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 __iter__(self):
        # 执行迭代器操作
        print('__iter__')

    def __reversed__(self):
        # 执行 reversed() 操作
        print('__reversed__')

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


obj = Foo()

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

6. 迭代器

iter, reversed__, next

迭代器的用法

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

for 语句也是调用上面两个方法来实现的

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()]

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__

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

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

__module__ 和 __class__">__module____class__

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

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

func.__code__">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__)
print(__file__) # 文件所在路径
print(__doc__) # 文件的注释

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

import filename

filename.__name__
filename.__file__
filename.__doc__

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

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


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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK