Jusene's Blog

python 类内置方法

字数统计: 1.3k阅读时长: 6 min
2018/06/11 Share

分类

  • 创建/销毁
  • 运算符重载
  • hash
  • bool
  • 可视化
  • 反射
  • 上下文管理
  • 大小
  • 描述器
  • 杂项

    运算符重载

1
class Point:
2
    def __init__(self,x,y):
3
        self.x = x
4
        self.y = y
5
    
6
    def __add__(self,other):
7
        return Point(self.x + other.x, self.y + other.y)
8
9
    def __sub__(self,other):
10
        return Point(self.x - other.x, self.y - other.y)
11
12
p = Point(1,2) + Point(2,3)
13
p.x
14
3
15
p.y
16
5

hash

使用内置函数hash对某个对象hash值时,会调用对象的__hash__方法

hash方法必须返回int

1
class Point:
2
    def __hash__(self):
3
        return None
4
5
set([Point(),])
6
TypeError: __hash__ method should return an integer
7
8
class Point:
9
    __hash__ = None
10
11
set([Point(),])
12
TypeError: unhashable type: 'Point'
13
14
class Point:
15
        pass
16
17
set([Point(),])
18
{<__main__.Point at 0x1057ee5f8>}

可hash的对象就是具有hash方法的对象

  • set类去重
1
class Point:
2
    def __init__(self,x,y):
3
        self.x = x
4
        self.y = y
5
6
p1 = Point(1,2)
7
p2 = Point(1,2)
8
set([p1,p2])
9
{<__main__.Point at 0x1068961d0>, <__main__.Point at 0x1068972e8>}
10
11
# 我们看下hash
12
hash(p1)
13
275289629
14
hash(p2)
15
-9223372036579485906
16
17
# 我们自己定义hash
18
class Point:
19
    def __init__(self,x,y):
20
        self.x = x
21
        self.y = y
22
    def __hash__(self):
23
        return hash('{}:{}'.format(self.x,self.y))
24
25
p1 = Point(1,2)
26
p2 = Point(1,2)
27
set([p1,p2])
28
{<__main__.Point at 0x1056fe4a8>, <__main__.Point at 0x1056fe240>}
29
30
# 还是没法去重,在看下hash
31
hash(p1)
32
-2893477644897778412
33
hash(p2)
34
-2893477644897778412
35
hash(p1) == hash(p2)  # hash 相等了
36
True
37
p1 == p2  # 实例对象却不相等了
38
False
39
40
class Point:
41
    def __init__(self,x,y):
42
        self.x = x
43
        self.y = y
44
    def __hash__(self):
45
        return hash('{}:{}'.format(self.x,self.y))
46
    def __eq__(self,other):
47
        return self.x == other.x and self.y == other.y
48
49
p1 = Point(1,2)
50
p2 = Point(1,2)
51
set([p1,p2])
52
{<__main__.Point at 0x10574b4e0>}    # 去重了

所以我们可以得出的结论:

  • 如果没有重写__hash__方法的话,hash方法通常不相等
  • 通常__hash____eq__一起使用,因为解释器通常判断hash是否相等及实例是否相等

定义类大小

1
class Sized:
2
    def __len__(self):
3
        return 10
4
5
len(Sized())
6
10

当对象实现len方法的时候,可以使用内置方法len求对象的长度,__len__方法必须返回非负整数

定义布尔

定义类bool

1
class F:
2
    def __bool__(self):
3
        return False
4
5
bool(F())
  • 当对象o定义了__bool__方法时,bool(o)返回值为o.__bool__()
  • 当对象o没有实现__bool__方法时, bool(o)永远返回True
  • 当对象o没有实现__bool__方法的时候,如果o实现了__len__方法,bool(o)返回值为len(o) != 0
  • 当对象o既没有实现__bool__方法,也没有实现__len__方法的时候,bool(o)返回True
  • 当同时实现__bool____len__方法的时候,__bool__的优先级更高
  • __bool__方法必须返回bool

对象可视化

1
class Point:
2
    def __init__(self,x,y):
3
        self.x = x
4
        self.y = y
5
    
6
    def __str__(self):
7
        return 'Point <{}, {}>'.format(self.x, self.y)
8
9
    def __repr__(self):
10
        return 'Point <{}, {}>'.format(self.x, self.y)
11
12
p = Point(1,2)

callable 对象

1
class Fn:
2
    def __call__(self):
3
        print('{} called'.format(self))
4
5
f= Fn()
6
f()
7
8
callable(f)
9
True

一个对象,只要实现了__call__方法,就可以通过小括号来调用,这一类对象,称为可调用对象

上下文管理

1
with open('test.txt') as f:
2
    pass

ope函数返回一个文件对象,而文件对象是上下文管理的

__enter____exit__魔术方法实现上文管理

1
class Context:
2
    def __enter__(self):
3
        print('enter context')
4
5
    def __exit__(self):
6
        print('exit context')
7
8
with Context():
9
    print('do something')

即使with块抛出异常,__enter____exit__也会被执行,所以上下文管理是安全的。

as子句可以获得__enter__的返回值

1
class Context:
2
    def __enter__(self,*args,**kwargs):
3
        print('enter context')
4
        print(args)
5
        print(kwargs)
6
        self.txt = 'hello world'
7
        return self
8
9
    def __exit__(self,*args,**kwargs):
10
        print('exit context')
11
12
with Context() as c:
13
    print(c.txt)
14
enter context
15
()
16
{}
17
hello world
18
exit context

__enter__除self之外,不带任何参数。

1
class Context:
2
    def __enter__(self):
3
        print('enter context')
4
5
    def __exit__(self,*args,**kwargs):
6
        print('exit context')
7
        print(args)
8
        print(kwargs)
9
        return self
10
11
with Context():
12
    pass
13
enter context
14
exit context
15
(None, None, None)
16
{}
17
18
with Context():
19
    raise Exception('raise')
20
enter context
21
exit context
22
(<class 'Exception'>, Exception('raise',), <traceback object at 0x1056e2488>)
23
{}

__exit__的返回值没法获取,如果with块中抛出异常,__exit__返回False的时候,会向上抛出异常,返回True,会屏蔽异常。
__exit__的三个参数 异常类型 异常 traceback

上下文管理应用

  • 计时

    1
    from functools import wraps
    2
    from datetime import datetime
    3
    4
    def timeit(fn):
    5
        @wraps(fn)
    6
        def wrap(*args,**kwargs):
    7
            start = datetime.now()
    8
            ret = fn(*args,**kwargs)
    9
            cost = datetime.now() - start
    10
            print(cost)
    11
            return ret
    12
        return wrap
    13
    14
    @timeit
    15
    def add(x,y):
    16
        return x+y
    1
    class timeit:
    2
        def __init__(self,fn=None):
    3
            wraps(fn)(self)
    4
    5
        def __call__(self,*args,**kwargs):
    6
            start = datetime.now()
    7
            ret = self.__wrapped__(*args,**kwargs)
    8
            cost = datetime.now() - start
    9
            print(cost)
    10
            return ret
    11
    12
        def __enter__(self):
    13
            self.start = datetime.now()
    14
    15
        def __exit__(self,*args):
    16
            cost = datetime.now() - self.start
    17
            print(cost)
    18
    19
    with timeit():
    20
        z=3+8
    21
    22
    @timeit
    23
    def add(x,y):
    24
        return x+y
  • 凡是要在代码块前后插入代码的场景统统适用

    • 资源管理
    • 权限验证
1
import contextlib
2
3
@contextlib.contextmanager
4
def context():
5
    print('enter context') # 初始化部分 相当于__enter__ 方法
6
    try:
7
        yield 'haha'   # 相当于__enter__的返回值
8
    finally:
9
        print('exit context') # 清理部分,相当于__exit__方法
CATALOG
  1. 1. 分类
  2. 2. 运算符重载
  3. 3. hash
  4. 4. 定义类大小
  5. 5. 定义布尔
  6. 6. 对象可视化
    1. 6.1. callable 对象
  7. 7. 上下文管理