内置方法 | 说明 |
__init__(self,...) | 初始化对象,在创建新对象时调用 |
__del__(self) | 释放对象,在对象被删除之前调用 |
__new__(cls,*args,**kwd) | 实例的生成操作 |
__str__(self) | 在使用print语句时被调用 |
__getitem__(self,key) | 获取序列的索引key对应的值,等价于seq[key] |
__len__(self) | 在调用内联函数len()时被调用 |
__cmp__(stc,dst) | 比较两个对象src和dst |
__getattr__(s,name) | 获取属性的值 |
__setattr__(s,name,value) | 设置属性的值 |
__delattr__(s,name) | 删除name属性 |
__getattribute__() | __getattribute__()功能与__getattr__()类似 |
__gt__(self,other) | 判断self对象是否大于other对象 |
__lt__(slef,other) | 判断self对象是否小于other对象 |
__ge__(slef,other) | 判断self对象是否大于或者等于other对象 |
__le__(slef,other) | 判断self对象是否小于或者等于other对象 |
__eq__(slef,other) | 判断self对象是否等于other对象 |
__call__(self,*args) | 把实例对象作为函数调用 |
__init__():
__init__方法在类的一个对象被建立时,马上运行。这个方法可以用来对你的对象做一些你希望的初始化。注意,这个名称的开始和结尾都是双下划线。
代码例子:
#!/usr/bin/python # Filename: class_init.py class Person: def __init__(self, name): self.name = name def sayHi(self): print 'Hello, my name is', self.name p = Person('Swaroop') 输出: |
__new__():
__new__()在__init__()之前被调用,用于生成实例对象.利用这个方法和类属性的特性可以实现设计模式中的单例模式.单例模式是指创建唯一对象吗,单例模式设计的类只能实例化一个对象.
#!/usr/bin/python # -*- coding: UTF-8 -*- class Singleton(object): def __init__(self): def __new__(cls, *args, **kwd): # 在__init__之前调用 |
__getattr__()、__setattr__()和__getattribute__():
当读取对象的某个属性时,python会自动调用__getattr__()方法.例如,fruit.color将转换为fruit.__getattr__(color).当使用赋值语句对属性进行设置时,python会自动调用__setattr__()方法.__getattribute__()的功能与__getattr__()类似,用于获取属性的值.但是__getattribute__()能提供更好的控制,代码更健壮.注意,python中并不存在__setattribute__()方法.
代码例子:
#!/usr/bin/python # -*- coding: UTF-8 -*- class Fruit(object): def __setattr__(self, name, value): if __name__ == "__main__": |
__getitem__():
如果类把某个属性定义为序列,可以使用__getitem__()输出序列属性中的某个元素.假设水果店中销售多钟水果,可以通过__getitem__()方法获取水果店中的没种水果
代码例子:
#!/usr/bin/python # -*- coding: UTF-8 -*- class FruitShop: if __name__ == "__main__": 输出为: |
__str__():
__str__()用于表示对象代表的含义,返回一个字符串.实现了__str__()方法后,可以直接使用print语句输出对象,也可以通过函数str()触发__str__()的执行.这样就把对象和字符串关联起来,便于某些程序的实现,可以用这个字符串来表示某个类
代码例子:
#!/usr/bin/python # -*- coding: UTF-8 -*- class Fruit: if __name__ == "__main__": |
__call__():
在类中实现__call__()方法,可以在对象创建时直接返回__call__()的内容.使用该方法可以模拟静态方法,实现了__call__的方法,则对象可以通过函数调用的方式 去执行__call__里面的内容
代码例子:
#!/usr/bin/python # -*- coding: UTF-8 -*- class Fruit: grow = Growth() # 调用Growth(),此时将类Growth作为函数返回,即为外部类Fruit定义方法grow(),grow()将执行__call__()内的代码 |
_formats = {
'ymd' : '{d.year}-{d.month}-{d.day}',
'mdy' : '{d.month}/{d.day}/{d.year}',
'dmy' : '{d.day}/{d.month}/{d.year}'
}
class Date:
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
def __format__(self, code):
if code == '':
code = 'ymd'
fmt = _formats[code]
return fmt.format(d=self)
from socket import socket, AF_INET, SOCK_STREAM
class LazyConnection:
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
self.address = address
self.family = family
self.type = type
self.sock = None
def __enter__(self):
if self.sock is not None:
raise RuntimeError('Already connected')
self.sock = socket(self.family, self.type)
self.sock.connect(self.address)
return self.sock
def __exit__(self, exc_ty, exc_val, tb):
self.sock.close()
self.sock = None
这个类的关键特点在于它表示了一个网络连接,但是初始化的时候并不会做任何事情(比如它并没有建立一个连接)。连接的建立和关闭是使用with 语句自动完成的。例如:
from functools import partial
conn = LazyConnection(('www.python.org', 80))
with conn as s:
s.send(b'GET /index.html HTTP/1.0\r\n')
s.send(b'Host: www.python.org\r\n')
s.send(b'\r\n')
resp = b''.join(iter(partial(s.recv, 8192), b''))
实现了socket的自动创建和关闭。
class Date:
__slots__ = ['year', 'month', 'day']
def __init__(self, year, month, day):
self.year = year
self.month = month
self.day = day
当你定义slots 后,Python 就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的数组来构建,而不是为每个实例定义一个字典,这跟元组或列表很类似。在slots 中列出的属性名在内部被映射到这个数组的指定小标上。使用slots 一个不好的地方就是我们不能再给实例添加新的属性了,只能使用在slots中定义的那些属性名.关于slots 的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用slots 可以达到这样的目的,但是这个并不是它的初衷。slots更多的是用来作为一个内存优化工具。
class A:
def __init__(self):
self._internal = 0 # An internal attribute
self.public = 1 # A public attribute
def public_method(self):
'''A public method'''
pass
def _internal_method(self):
pass
Python 并不会真的阻止别人访问内部名称。但是如果你这么做肯定是不好的,可能会导致脆弱的代码。你还可能会遇到在类定义中使用两个下划线( __) 开头的命名
class B:
def __init__(self):
self.__private = 0
def __private_method(self):
pass
def public_method(self):
pass
使用双下划线开始会导致访问名称变成其他形式。比如,在前面的类B 中,私有属性会被分别重命名为_B_private 和_B_private_method 。这时候你可能会问这样重命名的目的是什么,答案就是继承——这种属性通过继承是无法被覆盖的
class Integer:
def __init__(self, name):
self.name = name
def __get__(self, instance, cls):
if instance is None:
return self
else:
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, int):
raise TypeError('Expected an int')
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
一个描述器就是一个实现了三个核心属性访问操作(get、set、delete)的类, 分别为__get__()、__set__()、__delete__()这三个特殊的方法, 这些方法接收一个实例作为输入,之后相应的操作实例底层的字典。为了使用一个描述器,需要将这个描述器的实例作为类属性放到一个类的定义中,例如:
class Point:
x = Integer('x')
y = Integer('y')
def __init__(self, x, y):
self.x = x
self.y = y
当这样做之后,所有对描述器属性(x或者y)的访问都会被__get__()、__set__()、__delete__() 所捕获到。测试:
>>> p = Point(2, 3)
>>> p.x # Calls Point.x.__get__(p,Point)
2
>>> p.y = 5 # Calls Point.y.__set__(p, 5)
>>> p.x = 2.3 # Calls Point.x.__set__(p, 2.3)
Traceback (most recent call last):
File "", line 1, in
File "descrip.py", line 12, in __set__
raise TypeError('Expected an int')
TypeError: Expected an int
>>>
描述器的一个比较困惑的地方是它只能在类级别被定义,而不能为每个实例单独定义。因此下面的代码将无法工作:
# Does NOT work
class Point:
def __init__(self, x, y):
self.x = Integer('x') # No! Must be a class variable
self.y = Integer('y')
self.x = x
self.y = y
描述器通常是那些使用到装饰器或者元类的大型框架中的一个组件,同时它们的使用也 被隐藏在后面,下面是一个更高级的基于描述器的代码,并涉及到了一个类装饰器:
# Descriptor for a type-checked attribute
class Typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, cls):
if instance is None:
return self
else:
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError('Expected ' + str(self.expected_type))
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
# Class decorator that applies it to selected attributes
def typeassert(**kwargs):
def decorate(cls):
for name, expected_type in kwargs.items():
# Attach a Typed descriptor to the class
setattr(cls, name, Typed(name, expected_type))
return cls
return decorate
# Example use
@typeassert(name=str, shares=int, price=float)
class Stock:
def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price
Typed类描述器定义了 属性和预期属性类型的功能, typeassert 类装饰器实现了带 字典参数的类装饰器,通过传入的字典对所装饰的类遍历设置 描述器属性。 最后要指出的一点是,如果你只是想简单的自定义某个类的单个属性访问的话就不用去写描述器,这种情况下使用@property装饰器会更加容易。当程序中又很多重复代码的时候装饰器就有用了。
import math
class lazyproperty:
def __init__(self,func):
self._func = func
def __get__(self, instance, owner):
if instance is None:
return self
else:
value = self._func(instance)
setattr(instance,self._func.__name__,value)
return value
class Circle()
def __init__(self,radius):
self.radius = radius
@lazyproperty
def area(self):
print("Compute Area")
return math.pi * self.radius**2
当一个描述器被放入一个类的定义时,每次访问属性时它的__get__() __set__() 和__delete__()方法就会被触发,不过如果一个描述器仅仅只定义了 __get__()方法的话,它比通常就具有更弱的绑定,特别的,只有当被访问的属性不存在实例字典中时 __get__()方法才会被触发。如上例,当第一次访问 Circle对象的area属性的时候,实例字典中没有该属性,则进入描述器的__get__()方法,然后__get()方法通过 装饰器修饰的area函数名 和调用所得的值设置到实例中,此后该实例就有了 area方法。
class Base():
_fields = []
def __init__(self,*args):
if len(args) != len(self._fields):
raise TypeError("Expected {} arguments".format(len(self._fields)))
for name, value in zip(self._fields,args):
setattr(self,name,value)
class Stock(Base):
_fields = ['name','shares','price']
class Point(Base):
_fields = ['x','y']
class Base2():
_fields = []
def __init__(self,*args,**kwargs):
if len(args) > len(self._fields):
raise TypeError('Expected {} arguments'.format(len(self._fields)))
for name,value in zip(self._fields,args)
setattr(self,name,value)
for name in self._fields[len(args):]:
setattr(self,name,kwargs.pop(name))
if kwargs:
raise TypeError('invalid argument(s):{}'.format(','.join(kwargs)))
from abc import ABCMeta,abstractmethod
class ISteram(metaclass=ABCMeta):
@abstractmethod
def read(self,maxbytes = -1):
pass
@abstractmethod
def write(self,data):
pass
抽象类的一个特点是它不能被实例化。抽象类的目的是让别的类继承它并实现特定的抽象方法:
class SocketStream(ISteram):
def read(self,maxbytes = -1):
pass
def write(self,data):
pass
抽象基类的一个主要 用途是在代码中检查某些是否为特定类型,实现了特定的接口,比如:
def serialize(obj, stream):
if not isinstance(stream, IStream):
raise TypeError('Expected an IStream')
pass
除了继承这种方法之外,还可以通过注册方式来让某个类实现抽象基类:
import io
# Register the built-in I/O classes as supporting our interface
IStream.register(io.IOBase)
# Open a normal file and type check
f = open('foo.txt')
isinstance(f, IStream) # Returns True
@abstractmethod还能注解静态方法、类方法和 properties。你只需要保证这个注解紧靠在函数定义之前即可。
class D(metaclass=ABCMeta):
@property
@abstractmethod
def name(self):
pass
@name.setter
@abstractmethod
def name(self,value):
pass
@classmethod
@abstractmethod
def methon1(cls):
pass
@staticmethod
@abstractmethod
def method2():
pass
class Descriptor:
def __init__(self,name=None,**opts):
self.name = name
for key, value in opts.items():
setattr(self,key,value)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
class Typed(Descriptor):
excepted_type = type(None)
def __set__(self, instance, value):
if not isinstance(value,self.excepted_type):
raise TypeError('excepted ' + str(self.excepted_type))
super().__set__(instance,value)
class Unsigned(Descriptor):
def __set__(self, instance, value):
if value < 0:
raise ValueError('except >= 0')
super().__set__(instance,value)
class MaxSized(Descriptor):
def __init__(self,name=None,**opts):
if 'size' not in opts:
raise TypeError('miss size option')
super().__init__(name,**opts)
def __set__(self, instance, value):
if len(value) >= self.size:
raise ValueError('size must be < ' + str(self.size))
super().__set__(instance,value)
class Interger(Typed):
excepted_type = int
class UnsignedInterger(Interger,Unsigned):
pass
class Float(Typed):
excepted_type = float
class UnsignedFloat(Float,Unsigned):
pass
class String(Typed):
excepted_type = str
class SizedString(String,MaxSized):
pass
#使用这些自定义数据类型,我们自定义一个类
class Stock():
name = SizedString('name',size=8)
shares = UnsignedInterger('shares')
price = UnsignedFloat('price')
def __init__(self,name_,shares,price):
self.name = name_
self.shares = shares
self.price = price
s1 = Stock('acm',100,50.0)
import collections
class A(collections.Iterable):
def __init__(self,fruits):
self.fruits = fruits
def __iter__(self):
for item in self.fruits:
yield item
a = A(["a","b","c","d"])
for key in a:
print(key)
从collections.Iterable继承的类需要实现基类的抽象方法 __iter__() 否则会报错。
import collections
import bisect
class SortedItems(collections.Sequence):
def __init__(self,initial=None):
self._items = sorted(initial) if initial is not None else []
def __getitem__(self, index):
return self._items[index]
def __len__(self):
return len(self._items)
def add(self,item):
bisect.insort(self._items,item)
items = SortedItems([5,1,3])
print(list(items))
print((items[0],items[-1]))
items.add(2)
print(list(items))
print(items[0:2])
上面用到了bisect模块,它是一个在排序列表中插入元素的高效方式,可以保证元素插入后还保持顺序。 输出结果:
class A:
def spam(self,x):
pass
def foo(self):
pass
class B1():
def __init__(self):
self._a = A()
def spam(self,x):
return self._a.spam(x)
def foo(self):
return self._a.foo()
def bar(self):
pass
但是如果有大量的方法需要代理,那么使用 __getattr__()方法或许更好些。比如:
class B2():
def __init__(self):
self._a = A()
def bar(self):
pass
def __getattr__(self, item):
"这个方法在访问的attribute不存在的时候调用"
return getattr(self._a,item)
当去访问对象的属性的时候,最开始B2没有 方法属性 spam 和 foo ,当代码中去调用改方法的时候 会执行__getattr__方法,然后从A对象的实例中返回对应的属性,即返回a实例中的方法,实现代理。
class Proxy:
def __init__(self,obj):
self._obj = obj
def __getattr__(self, item):
return getattr(self._obj,item)
def __setattr__(self, key, value):
if key.startswith('_'):
super().__setattr__(key,value)
else:
setattr(self._obj,key,value)
def __delattr__(self, item):
if item.startswith('_'):
super().__delattr__(item)
else:
delattr(self._obj,item)
class Spam:
def __init__(self,x):
self.x = x
def bar(self,y):
print('Spam.bar:',self.x,y)
__getattr__()实际是一个后备方法,只有在属性不存在的时候才会调用。因此,如果代理类实例本身有这个属性的话,那么久不会触发这个方法的。另外__setattr__() 和 __delattr__() 需要额外的魔法来区分代理实例和被代理实例的_obj属性。一个通常的预定是只代理那些不以下划线开头的属性。
import time
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
@classmethod
def today(cls):
t = time.localtime()
return cls(t.tm_year,t.tm_mon,t.tm_mday)
class LoggedMappingMixin:
"添加logging到get set delete 操作,在调试模式下"
__slots__ = () #混入类没有实例变量,因为直接实例化混入类没有任何意义
def __getitem__(self, item):
print("Getting "+ str(item))
return super().__getitem__(item)
def __setitem__(self, key, value):
print("Setting {} = {!r}".format(key,value))
return super().__setitem__(key,value)
def __delitem__(self, key):
print("Delete " + str(key))
return super().__delitem__(key)
class SetOnceMappingMixin:
"仅仅允许值被设置一次"
__slots__ = ()
def __setitem__(self, key, value):
if key in self:
raise KeyError(str(key)+ ' already set')
else:
return super().__setitem__(key,value)
class StringKeysMappingMixin:
"键值只允许是字符串"
__slots__ = ()
def __setitem__(self, key, value):
if not isinstance(key,str):
raise TypeError('key must be strings')
else:
super().__setitem__(key,value)
这些类单独使用起来没有任何意义,事实上如果你去实例化任何一个类,除了产生异常外没任何作用。它们是用来通过多继承来和其他映射对象混入使用的。例如:
class LoggedDict(LoggedMappingMixin,dict):
pass
d = LoggedDict()
d['x'] = 23
print(d['x'])
del d['x']
输出结果:
def LoggedMapping(cls):
""" 第二种方式:使用类装饰器"""
cls_getitem = cls.__getitem__
cls_setitem = cls.__setitem__
cls_delitem = cls.__delitem__
def __getitem__(self, key):
print('Getting ' + str(key))
return cls_getitem(self, key)
def __setitem__(self, key, value):
print('Setting {} = {!r}'.format(key, value))
return cls_setitem(self, key, value)
def __delitem__(self, key):
print('Deleting ' + str(key))
return cls_delitem(self, key)
cls.__getitem__ = __getitem__
cls.__setitem__ = __setitem__
cls.__delitem__ = __delitem__
return cls
@LoggedMapping
class LoggedDict(dict):
pass
class Connection:
def __init__(self):
self.new_state(ClosedConnectionState)
def new_state(self,newstate):
self._state = newstate
def read(self):
return self._state.read(self)
def write(self,data):
return self._state.write(self,data)
def open(self):
return self._state.open(self)
def close(self):
return self._state.close(self)
class ConnectionState:
@staticmethod
def read(conn):
raise NotImplementedError()
@staticmethod
def write(conn,data):
raise NotImplementedError()
@staticmethod
def open(conn):
raise NotImplementedError()
@staticmethod
def close(conn):
raise NotImplementedError()
class CloseConnectionState(ConnectionState):
@staticmethod
def read(conn):
raise RuntimeError('Not open')
@staticmethod
def write(conn,data):
raise RuntimeError('Not open')
@staticmethod
def open(conn):
conn.new_state(OpenConnectionState)
@staticmethod
def close(conn):
raise RuntimeError('alread closed')
class OpenConnectionState:
@staticmethod
def read(conn):
print('reading')
@staticmethod
def write(conn,data):
print('write')
@staticmethod
def open(conn):
raise RuntimeError('already open')
@staticmethod
def close(conn):
conn.new_state(CloseConnectionState)
16、让类支持比较操作