类和对象

改变对象的字符串显示

问题:

  你想改变对象实例的打印或显示输出,让它们更具可读性

解决方案:

  要改变一个实例的字符串表示,可重新定义它的str () 和repr () 方法。例如:

 1 class Pair:
 2     def __init__(self, x, y):
 3         self.x = x
 4         self.y = y
 5 
 6     def __repr__(self):
 7         #另外一种写法 return 'Pair(%r,%r)'%(self.x, self.y )
 8         return 'Pair({0.x!r}, {0.y!r})'.format(self)
 9 
10     def __str__(self):
11         return '({0.x!s}, {0.y!s})'.format(self)
12 
13 p = Pair(3, 4)
14 
15 print("__str__()的输出效果:", p)
16 
17 print('__repr__()的输出效果:', repr(p))

以上代码执行的结果为:

__str__()的输出效果: (3, 4)
__repr__()的输出效果: Pair(3, 4)

自定义字符串的格式化

问题:

  你想通过format() 函数和字符串方法使得一个对象能支持自定义的格式化。

解决方案:

  为了自定义字符串的格式化,我们需要在类上面定义format () 方法。例如:

 1 _formats = {
 2     'ymd' : '{d.year}-{d.month}-{d.day}',
 3     'mdy' : '{d.month}/{d.day}/{d.year}',
 4     'dmy' : '{d.day}/{d.month}/{d.year}'
 5     }
 6 
 7 class Date:
 8     def __init__(self, year, month, day):
 9         self.year = year
10         self.month = month
11         self.day = day
12 
13     def __format__(self, format_spec):
14         if format_spec == '':
15             format_spec = 'ymd'
16 
17         fmt = _formats[format_spec]
18         return fmt.format(d=self)
19 
20 d = Date(2017, 8, 7)
21 print('默认格式的输出:', format(d))
22 
23 print('mdy类型的输出:', format(d, 'mdy'))
24 
25 print('The date is {:ymd}'.format(d))
26 
27 print('The date is {:mdy}'.format(d))
28 
29 from datetime import  date
30 d = date(2017, 8, 7)
31 print(format(d))
32 
33 print(format(d,'%A, %B %d, %Y'))
34 
35 print('The end is {:%d %b %Y}. Goodbye'.format(d))

以上代码执行的结果为:

默认格式的输出: 2017-8-7
mdy类型的输出: 8/7/2017
The date is 2017-8-7
The date is 8/7/2017
2017-08-07
Monday, August 07, 2017
The end is 07 Aug 2017. Goodbye

让对象支持上下文管理协议

问题:

  你想让你的对象支持上下文管理协议(with 语句) 

解决方案:  

  为了让一个对象兼容with 语句,你需要实现enter () 和exit () 方法。例如,考虑如下的一个类,它能为我们创建一个网络连接:

 1 from socket import socket,AF_INET,SOCK_STREAM
 2 
 3 class LazyConnection:
 4     '''
 5     连接端
 6     '''
 7     def __init__(self, addresss, family=AF_INET, type=SOCK_STREAM):
 8         self.address = addresss
 9         self.family = family
10         self.type = type
11         self.sock = None
12 
13     def __enter__(self):
14         if self.sock is not None:
15             raise RuntimeError('Already connected')
16 
17         self.sock = socket(self.family, self.type)
18         self.sock.connect(self.address)
19         return self.sock
20 
21     def __exit__(self, exc_type, exc_val, exc_tb):
22         self.sock.close()
23         self.sock = None
24 
25 
26 from functools import partial
27 
28 conn = LazyConnection(('www.python.org', 80))
29 
30 with conn as s:
31     '''
32     通过with管理上下文
33     '''
34     # conn.__enter__() executes: connection open
35     s.send(b'GET /index.html HTTP/1.0
')
36     s.send(b'Host: www.python.org
')
37     s.send(b'
')
38     resp = b''.join(iter(partial(s.recv, 8192), b'')).decode('utf-8')
39     # conn.__exit__() executes: connection closed
40     print(resp)

以上代码执行的结果为:

HTTP/1.1 301 Moved Permanently
Server: Varnish
Retry-After: 0
Location: https://www.python.org/index.html
Content-Length: 0
Accept-Ranges: bytes
Date: Mon, 07 Aug 2017 02:34:49 GMT
Via: 1.1 varnish
Connection: close
X-Served-By: cache-nrt6134-NRT
X-Cache: HIT
X-Cache-Hits: 0
X-Timer: S1502073289.426123,VS0,VE0
Strict-Transport-Security: max-age=63072000; includeSubDomains

总结:  

  编写上下文管理器的主要原理是你的代码会放到with 语句块中执行。当出现with语句的时候,对象的enter () 方法被触发,它返回的值(如果有的话) 会被赋值给as 声明的变量。然后,with 语句块里面的代码开始执行。最后, exit () 方法被触发进行清理工作。

  不管with 代码块中发生什么,上面的控制流都会执行完,就算代码块中发生了异常也是一样的。事实上, exit () 方法的第三个参数包含了异常类型、异常值和追溯信息(如果有的话)。exit () 方法能自己决定怎样利用这个异常信息,或者忽略它并返回一个None 值。如果exit () 返回True ,那么异常会被清空,就好像什么都没发生一样, with 语句后面的程序继续在正常执行。

创建大量对象时节省内存方法

问题:

  你的程序要创建大量(可能上百万) 的对象,导致占用很大的内存

解决方案:

  对于主要是用来当成简单的数据结构的类而言,你可以通过给类添加slots 属性来极大的减少实例所占的内存。比如:

1 class Date:
2     __slots__ =  ['year', 'month', 'day']
3     def __init__(self, year, month, day):
4         self.year = year
5         self.month = month
6         self.day = day

注意:

  当你定义slots 后,Python 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在slots 中列出的属性名在内部被映射到这个数组的指定小标上。使用slots 一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在slots中定义的那些属性名

在类中封装属性名

问题:

  你想封装类的实例上面的“私有”数据,但是Python 语言并没有访问控制

解决方案: 

  Python 程序员不去依赖语言特性去封装数据,而是通过遵循一定的属性和方法命名规约来达到这个效果。第一个约定是任何以单下划线_开头的名字都应该是内部实现。比如:

 1 class A:
 2     def __init__(self):
 3         self._internal = 0
 4         self.public = 1
 5 
 6     def public_method(self):
 7         print('public method')
 8 
 9     def _internal_method(self):
10         print('internal method')

你还可能会遇到在类定义中使用两个下划线(__ ) 开头的命名。比如:

 1 class B:
 2     def __init__(self):
 3         self.__private = 0
 4 
 5     def __private_method(self):
 6         print('private method...外部不能调用,只能内部中调用')
 7 
 8     def public_method(self):
 9         print('公用方法调用内部的__private_method..')
10         self.__private_method()

  使用双下划线开始会导致访问名称变成其他形式。比如,在前面的类B 中,私有属性会被分别重命名为_B__ private 和_B __private method 。这时候你可能会问这样重命名的目的是什么,答案就是继承——这种属性通过继承是无法被覆盖的。比如:

 1 class C(B):
 2     '''
 3     C类中的__private 在外部调用为_C__private
 4     C类中的__private_method在外部调用为_C__private_method
 5     '''
 6 
 7     def __init__(self):
 8         super().__init__()
 9         self.__private = 1              #这样写就不会覆盖掉B中的__private
10 
11         def __private_mothod(self):     #同样不会覆盖掉B中的__private_method
12             print('C类中的private_mothd')

总结:

  上面提到有两种不同的编码约定(单下划线和双下划线) 来命名私有属性,那么问题就来了:到底哪种方式好呢?大多数而言,你应该让你的非公共名称以单下划线开头。但是,如果你清楚你的代码会涉及到子类,并且有些内部属性应该在子类中隐藏起来,那么才考虑使用双下划线方案。

 

创建可管理的属性

问题:

  你想给某个实例attribute 增加除访问与修改之外的其他处理逻辑,比如类型检查或合法性验证

解决方案:

  自定义某个属性的一种简单方法是将它定义为一个property。例如,下面的代码定义了一个property,增加对一个属性简单的类型检查:

 1 class Person:
 2     def __init__(self, first_name):
 3         self.first_name = first_name
 4 
 5     @property                                       #把方法变成一个属性,外部调用的时候,不需要加上()
 6     def first_name(self):
 7         return self._first_name
 8 
 9     @first_name.setter                              #给frist_name方法接收一个值作为self._first_name的返回
10     def first_name(self, value):
11         if not isinstance(value, str):
12             raise TypeError('Expected a string')
13         self._first_name = value
14 
15     @first_name.deleter                             #如果first_name属性没被创建则报错
16     def first_name(self):
17         raise ArithmeticError("Can't delete attribute")
18 
19 a = Person('demon')
20 print(a.first_name)
21 
22 #a.first_name = 50          #报错raise TypeError('Expected a string')
23 
24 #del a.first_name           #报错ArithmeticError: Can't delete attribute

以上代码执行的结果为:

demon

 使用已存在的get 和set 方法基础上定义property。例如:

 1 class Person:
 2     def __init__(self, first_name):
 3         self.set_first_name(first_name)
 4 
 5     def get_first_name(self):
 6         return self._first_name
 7 
 8     def set_first_name(self, value):
 9         if not isinstance(value, str):
10             raise TypeError('Expected a string')
11         self._first_name = value
12 
13     def del_first_name(self):
14         raise AttributeError("Can't delete attribute")
15 
16     name = property(get_first_name, set_first_name, del_first_name)
17     

调用父类方法

问题:

  你想在子类中调用父类的某个已经被覆盖的方法 

解决方案:

  为了调用父类(超类) 的一个方法,可以使用super() 函数,比如:

 1 class A:
 2     def spam(self):
 3         print('A.spam')
 4 
 5 class B(A):
 6     def spam(self):
 7         print('B.spam')
 8         super().spam()
 9 
10 a = A()
11 b = B()
12 
13 a.spam()            #调用A类中的spam方法
14 print('我是分割符'.center(50, '*'))
15 b.spam()            #先调用B类中的spam方法,然后通过super继承A类中的spam方法返回输出

以上代码执行的结果为:

A.spam
**********************我是分割符***********************
B.spam
A.spam

super() 函数的一个常见用法是在init () 方法中确保父类被正确的初始化了:

1 class A:
2     def __init__(self):
3         self.x = 0
4 
5 class B(A):
6     def __init__(self):
7         super().__init__()
8         self.y = 1
9         

super() 的另外一个常见用法出现在覆盖Python 特殊方法的代码中,比如:

 1 class Proxy:
 2     def __init__(self, obj):
 3         self._obj = obj
 4 
 5     def __getattr__(self, name):
 6         return getattr(self._obj, name)
 7 
 8     def __setattr__(self, name, value):
 9         if name.startswith('_'):
10             super().__setattr__(name, value)            #__setattr__()实现包含一个名字检查,如果某个属性名称以_开头,就通过super调用
11         else:
12             setattr(self._obj, name , value)            #否则就委派给内部的_obj来处理

  在上面代码中, __setattr__ () 的实现包含一个名字检查。如果某个属性名以下划线( _) 开头,就通过super() 调用原始的__setattr__ () ,否则的话就委派给内部的代理对象self. _obj 去处理。这看上去有点意思,因为就算没有显式的指明某个类的父类, super() 仍然可以有效的工作。

子类中扩展property

问题:

  在子类中,你想要扩展定义在父类中的property 的功能

解决方案:

  考虑如下的代码,它定义了一个property:

 1 class  Person:
 2     def __init__(self, name):
 3         self._name = name
 4 
 5     @property
 6     def name(self):
 7         return self._name
 8 
 9     @name.setter
10     def name(self, value):
11         if not isinstance(value, str):
12             raise TypeError('Expected a string')
13         self._name = value
14 
15     @name.deleter
16     def name(self):
17         raise AttributeError("Can't delete attribute")
18 
19 
20 class SubPerson(Person):
21     @Person.name.getter
22     def name(self):
23         print('Getting name')
24         return super().name
25 
26     @Person.name.setter
27     def name(self, value):
28         print('Setting name to', value)
29         super(SubPerson, SubPerson).name.__set__(self, value)

创建新的类或实例属性

问题:

  你想创建一个新的拥有一些额外功能的实例属性类型,比如类型检查 

解决方案:

  如果你想创建一个全新的实例属性,可以通过一个描述器类的形式来定义它的功能。下面是一个例子:

 1 class Integer:
 2     def __init__(self, name):
 3         self.name = name
 4 
 5     def __get__(self, instance, cls):
 6         if instance is None:
 7             return self
 8         else:
 9             return instance.__dict__[self.name]
10 
11     def __set__(self, instance, value):
12         if not isinstance(value, int):
13             raise TypeError('Expected an int')
14         instance.__dict__[self.name] = value
15 
16     def __delattr__(self, instance):
17         del instance.__dict__[self.name]
18 
19 
20 class Point:
21     x = Integer('x')
22     y = Integer('y')
23 
24     def __init__(self, x, y):
25         self.x = x
26         self.y = y
27 
28 p = Point(2, 3)
29 print(p.x)                  #x.__get__(p, Point)
30 print(p.y)
31 
32 print(Point.x)              #x.__get__(None, Point) 

super() 的另外一个常见用法出现在覆盖Python 特殊方法的代码中,比如:

2
3
<__main__.Integer object at 0x101c456a0>

使用延迟计算属性

问题:

  你想将一个只读属性定义成一个property,并且只在访问的时候才会计算结果。但是一旦被访问后,你希望结果值被缓存起来,不用每次都去计算

解决方案: 

  定义一个延迟属性的一种高效方法是通过使用一个描述器类,如下所示:

 1 class lazyproperty:
 2     def __init__(self, func):
 3         self.func = func
 4 
 5     def __get__(self, instance, cls):
 6         if instance is None:
 7             return self
 8         else:
 9             value = self.func(instance)
10             setattr(instance, self.func.__name__, value)
11             return value
12 
13 
14 import math
15 
16 
17 class Circle:
18     def __init__(self, radius):
19         self.radius = radius
20 
21     @lazyproperty
22     def area(self):
23         print('Computing area')
24         return math.pi * self.radius ** 2
25 
26     @lazyproperty
27     def perimeter(self):
28         print('Computing perimeter')
29         return 2 * math.pi * self.radius
30 
31 
32 c = Circle(4.0)
33 print('radius:', c.radius)
34 print('area:', c.area)
35 print('perimeter:', c.perimeter)

以上代码执行的结果为:

radius: 4.0
Computing area
area: 50.26548245743669
Computing perimeter
perimeter: 25.132741228718345

简化数据结构的初始化

问题:

  你写了很多仅仅用作数据结构的类,不想写太多烦人的init () 函数

解决方案:

  可以在一个基类中写一个公用的init () 函数:

 1 import math
 2 
 3 
 4 class Structurel:
 5     _fields = []
 6 
 7     def __init__(self, *args):
 8         if len(args) != len(self._fields):
 9             raise TypeError('Expected {} arguments'.format(len(self._fields)))
10         for name,value in zip(self._fields, args):
11             setattr(self, name, value)
12 
13 
14 class Stock(Structurel):
15     _fields = ['name', 'shares', 'price']
16 
17 
18 class Point(Structurel):
19     _fields = ['x', 'y']
20 
21 
22 class Circle(Structurel):
23     _fields = ['radius']
24 
25     def area(self):
26         return math.pi * self.radius ** 2
27 
28 s = Stock('ACME', 50, 91.1)
29 p = Point(2, 3)
30 c = Circle(4.5)
31 print(c.area())
32 # c = Stock('ACME', 50)         #报错,会抛出raise的TypeError的异常

以上代码执行的结果为:

63.61725123519331

定义接口或者抽象基类

问题:

  你想定义一个接口或抽象类,并且通过执行类型检查来确保子类实现了某些特定的方法

解决方案:

  使用abc 模块可以很轻松的定义抽象基类:

 1 from abc import ABCMeta, abstractclassmethod
 2 
 3 class IStream(metaclass=ABCMeta):
 4     @abstractclassmethod
 5     def read(self, maxbytes=-1):
 6         pass
 7 
 8     @abstractclassmethod
 9     def write(self, data):
10         pass
11 
12 #抽象类的一个特点是它不能直接被实例化,比如你想像下面这样做是不行的
13 #a = IStream()  TypeError Can't instantiate abstract class
14 
15 #抽象类的目的就是让别的类继承它并实现特定的抽象方法:
16 class SocketStream(IStream):
17     def read(self, maxbytes=-1):
18         pass
19 
20     def write(self, data):
21         pass

实现数据模型的类型约束

问题: 

  你想定义某些在属性赋值上面有限制的数据结构

解决方案:

  在这个问题中,你需要在对某些实例属性赋值时进行检查。所以你要自定义属性赋值函数,这种情况下最好使用描述

 1 class Descriptor:
 2     def __init__(self, name=None, **opts):
 3         self.name = name
 4         for key,value in opts.items():
 5             setattr(self, key, value)
 6 
 7     def __set__(self, instance, value):
 8         instance.__dict__[self.name] = value
 9 
10 
11 class Typed(Descriptor):
12     expected_type = type(None)
13 
14     def __set__(self, instance, value):
15         if not isinstance(instance, self.expected_type):
16             raise TypeError('expected ' + str(self.expected_type))
17         super().__set__(instance, value)
18 
19 
20 class Unsigned(Descriptor):
21     def __set__(self, instance, value):
22         if value <= 0:
23             raise ValueError('Expected >= 0')
24         super().__set__(instance, value)
25 
26 
27 class MaxSized(Descriptor):
28     def __init__(self, name=None, **opts):
29         if 'size' not in opts:
30             raise TypeError('missing size option')
31         super().__init__(name, **opts)
32 
33     def __set__(self, instance, value):
34         if len(value) >= self.size:
35             raise ValueError('size must be < ' + str(self.size))
36         super().__set__(instance, value)
37 
38 
39 class Integer(Typed):
40     expected_type = int
41 
42 
43 class UnsignedInteger(Integer, Unsigned):
44     pass
45 
46 
47 class Float(Typed):
48     expected_type = float
49 
50 
51 class UnsignedFloat(Float, Unsigned):
52     pass
53 
54 
55 class String(Typed):
56     expected_type = str
57 
58 
59 class SizedString(String, MaxSized):
60     pass
61 
62 
63 class Stock:
64     name = SizedString('name', size=8)
65     shares = UnsignedInteger('shares')
66     price = UnsignedFloat('price')
67 
68     def __init__(self, name, shares, price):
69         self.name = name
70         self.shares = shares
71         self.price = price

实现自定义容器

问题:

  你想实现一个自定义的类来模拟内置的容器类功能,比如列表和字典。但是你不确定到底要实现哪些方法

解决方案:

  collections 定义了很多抽象基类,当你想自定义容器类的时候它们会非常有用。比如你想让你的类支持迭代,那就让你的类继承collections.Iterable 即可:

 1 from collections import Iterable
 2 
 3 
 4 class A(Iterable):
 5 
 6     def __iter__(self):
 7         return iter()
 8 
 9 
10 
11 
12 #另外一种实现的方式
13 import bisect
14 import collections
15 
16 class SortedItem(collections.Sequence):
17     def __init__(self, initial=None):
18         self._item = sorted(initial) if initial is not None else []
19 
20     def __getitem__(self, index):
21         return self._item[index]
22 
23     def __len__(self):
24         return len(self._item)
25 
26     def add(self, item):
27         bisect.insort(self._item, item)                 #可以保证元素插入后还保持顺序
28 
29 
30 items = SortedItem([5, 1, 3])
31 print('列表排序的结果'.center(30, '*'))
32 print(list(items))
33 print('获取列表索引位置的值'.center(30, '-'))
34 print(items[0], items[-1])
35 print('添加元素以后的列表'.center(30, '*'))
36 items.add(2)
37 print(list(items))

以上代码执行的结果为:

***********列表排序的结果************
[1, 3, 5]
----------获取列表索引位置的值----------
1 5
**********添加元素以后的列表***********
[1, 2, 3, 5]

collections 中很多抽象类会为一些常见容器操作提供默认的实现, 这样一来你只需要实现那些你最感兴趣的方法即可。假设你的类继承自collections.MutableSequence ,如下:

 1 import collections
 2 
 3 class Item(collections.MutableSequence):
 4     def __init__(self, initial=None):
 5         self._item = initial if initial is not None else []
 6 
 7     def __getitem__(self, index):
 8         print('Getting:', index)
 9         return self._item[index]
10 
11     def __setitem__(self, index, value):
12         print('Setting', index, value)
13         self._item[index] = value
14 
15     def __delitem__(self, index):
16         print('Deleting:', index)
17         del self._item[index]
18 
19     def insert(self, index, value):
20         print('Inserting:', index, value)
21         self._item.insert(index, value)
22 
23     def __len__(self):
24         return len(self._item)
25 
26 a = Item([1, 2, 3, 4, 5])
27 print('长度:', len(a))
28 
29 a.append(10)
30 a.append(2)
31 print(list(a))
32 print('2在a列表中出现的次数:', a.count(2))
33 a.remove(10)
34 print('删除列表中元素为10:', list(a))

以上代码执行的结果为:

长度: 5
Inserting: 5 10
Inserting: 6 2
Getting: 0
Getting: 1
Getting: 2
Getting: 3
Getting: 4
Getting: 5
Getting: 6
Getting: 7
[1, 2, 3, 4, 5, 10, 2]
Getting: 0
Getting: 1
Getting: 2
Getting: 3
Getting: 4
Getting: 5
Getting: 6
Getting: 7
2在a列表中出现的次数: 2
Getting: 0
Getting: 1
Getting: 2
Getting: 3
Getting: 4
Getting: 5
Deleting: 5
Getting: 0
Getting: 1
Getting: 2
Getting: 3
Getting: 4
Getting: 5
Getting: 6
删除列表中元素为10: [1, 2, 3, 4, 5, 2]

属性的代理访问

问题:

  你想将某个实例的属性访问代理到内部另一个实例中去,目的可能是作为继承的一个替代方法或者实现代理模式

解决方案:

  简单来说,代理是一种编程模式,它将某个操作转移给另外一个对象来实现。最简单的形式可能是像下面这样:

 1 class A:
 2     def spam(self, x):
 3         self._x = x
 4 
 5     def foo(self):
 6         pass
 7 
 8 
 9 class B1:
10     '''简单代理'''
11     def __init__(self):
12         self._a = A()
13 
14     def spam(self, x):
15         return self._a.spam(x)
16 
17     def foo(self):
18         return self._a.foo()
19 
20     def bar(self):
21         pass
22 
23 
24 #如果有大量的方法需要代理,那么使用getattr () 方法或许或更好些
25 class B2:
26     def __init__(self):
27         self._a = A()
28 
29     def bar(self):
30         pass
31 
32     def __getattr__(self, name):
33         return getattr(self._a, name)

另外一个代理例子是实现代理模式,例如:

 1 #简单的实现代理模式
 2 class Proxy:
 3     def __init__(self, obj):
 4         self._obj = obj
 5 
 6     def __getattr__(self, name):
 7         print('getattr:', name)
 8         return getattr(self._obj, name)
 9 
10     def __setattr__(self, name, value):
11         if name.startswith('_'):
12             super().__setattr__(name, value)
13         else:
14             print('setattr:', name, value)
15             setattr(self._obj, name, value)
16 
17     def __delattr__(self, name):
18         if name.startswith('_'):
19             super().__delattr__(name)
20         else:
21             print('delattr:', name)
22             delattr(self._obj, name)
23 
24 
25 class Spam:
26     def __init__(self, x):
27         self.x = x
28 
29     def bar(self, y):
30         print('Spam.bar:', self.x, y)
31 
32 
33 #实例化Spam
34 s = Spam(2)
35 #实例化Proxy,并把实例化的Spam传入进去
36 p = Proxy(s)
37 #调用Proxy中的getattr
38 print(p.x)
39 #调用Proxy中的bar方法
40 p.bar(3)
41 #切换x值
42 p.x = 30
43 print(p.x)

以上代码执行的结果为:

getattr: x
2
getattr: bar
Spam.bar: 2 3
setattr: x 30
getattr: x
30

在类中定义多个构造器

问题:

  你想实现一个类,除了使用init () 方法外,还有其他方式可以初始化它

解决方案:

  为了实现多个构造器,你需要使用到类方法。例如:

 1 import time
 2 
 3 
 4 class Date:
 5     '''
 6     方法一:使用类方法
 7     '''
 8     def __init__(self, year, month, day):
 9         self.year = year
10         self.month = month
11         self.day = day
12 
13     @classmethod
14     def today(cls):
15         t = time.localtime()
16         return cls(t.tm_year, t.tm_mon, t.tm_mday)
17 
18 
19 
20 class NewDate(Date):
21     pass
22 
23 a = Date(2017, 8, 8)
24 b = Date.today()
25 c = NewDate.today()     #创建并返回最终的实例

创建不调用init 方法的实例

 问题:

  你想创建一个实例,但是希望绕过执行init () 方法

解决方案:

  可以通过new () 方法创建一个未初始化的实例。例如考虑如下这个类:

 1 class Date:
 2     def __init__(self, year, month, day):
 3         self.year = year
 4         self.month = month
 5         self.day = day
 6 
 7 d = Date(2017, 8, 8)
 8 print('可以使用init来初始化的:', d.year)
 9 
10 '''
11 d1 = Date.__new__(Date)
12 print(d1.day)
13 #会报这个错误 AttributeError: 'Date' object has no attribute 'day'
14 '''
15 #解决方法如下.这个Date day 还不存在,所以你需要手动初始化
16 data = {'year':2017, 'month':8 , 'day':8}
17 d1 = Date.__new__(Date)
18 for key,value in data.items():
19     setattr(d1, key, value)
20 
21 print('需要手动设置并初始化的(new):', d1.day)

以上代码执行的结果为:

可以使用init来初始化的: 2017
需要手动设置并初始化的(new): 8

当我们在反序列对象或者实现某个类方法构造函数时需要绕过init () 方法来创建对象。例如,对于上面的Date 来来讲,有时候你可能会像下面这样定义一个新的构造函数today() :

 1 from time import localtime
 2 
 3 class Date:
 4     def __init__(self, year, month, day):
 5         self.year = year
 6         self.month = month
 7         self.day = day
 8 
 9     @classmethod
10     def today(cls):
11         d = cls.__new__(cls)
12         t = localtime()
13         d.year = t.tm_year
14         d.month = t.tm_mon
15         d.day = t.tm_mday
16         return d
17 
18 d = Date(2017, 8, 8)

让类支持比较操作

问题:

  你想让某个类的实例支持标准的比较运算(比如>=,!=,<=,< 等),但是又不想去实现那一大丢的特殊方法

解决方案:

  装饰器functools.total ordering 就是用来简化这个处理的。使用它来装饰一个来,你只需定义一个eq () 方法,外加其他方法( lt , le , gt , or ge ) 中的一个即可。然后装饰器会自动为你填充其它比较方法

  作为例子,我们构建一些房子,然后给它们增加一些房间,最后通过房子大小来比较它们:

 1 from  functools import total_ordering
 2 
 3 
 4 class Room:
 5     def __init__(self, name, length, width):
 6         self.name = name
 7         self.length = length
 8         self.width = width
 9         self.square_feet = self.length * self.width
10 
11 
12 @total_ordering
13 class House:
14     def __init__(self, name, style):
15         self.name = name
16         self.style = style
17         self.rooms = list()
18 
19     @property
20     def living_space_footage(self):
21         return sum(r.square_feet for r in self.rooms)
22 
23     def add_room(self, room):
24         self.rooms.append(room)
25 
26     def __str__(self):
27         '{}: {} square foot {}'.format(self.name,
28                                        self.living_space_footage,
29                                        self.style)
30 
31     def __eq__(self, other):
32         return self.living_space_footage == other.living_space_footage
33 
34     def __lt__(self, other):
35         return self.living_space_footage < other.living_space_footage
36 
37     def __gt__(self, other):
38         return self.living_space_footage > other.living_space_footage
39 
40 
41 h1 = House('h1', 'Cape')
42 h1.add_room(Room('Master Bedroom', 14, 21))
43 h1.add_room(Room('Living Room', 18, 20))
44 h1.add_room(Room('Kitchen', 12, 16))
45 h1.add_room(Room('Office', 12, 12))
46 h2 = House('h2', 'Ranch')
47 h2.add_room(Room('Master Bedroom', 14, 21))
48 h2.add_room(Room('Living Room', 18, 20))
49 h2.add_room(Room('Kitchen', 12, 16))
50 h3 = House('h3', 'Split')
51 h3.add_room(Room('Master Bedroom', 14, 21))
52 h3.add_room(Room('Living Room', 18, 20))
53 h3.add_room(Room('Office', 12, 16))
54 h3.add_room(Room('Kitchen', 15, 17))
55 
56 houses = [h1, h2, h3]
57 
58 print('Is h1 bigger than h2?', h1 > h2) # prints True
59 print('Is h2 smaller than h3?', h2 < h3) # prints True
60 print('Is h2 greater than or equal to h1?', h2 >= h1) # Prints False
61 #print('Which one is biggest?', max(houses)) # Prints 'h3: 1101-square-foot Split'
62 #print('Which is smallest?', min(houses)) # Prints 'h2: 846-square-foot Ranch

 以上代码执行的结果为:

Is h1 bigger than h2? True
Is h2 smaller than h3? True
Is h2 greater than or equal to h1? False
原文地址:https://www.cnblogs.com/demon89/p/7296122.html