Python运算符重载

1、__getattr__,__setattr__,__getattribute__等等

 

class A(object):
   age = 43
   def __init__(self):
       print 'A'
       self.name = 'a'
       self.age = 23

   def __getattr__(self, item):
        if item == 'height':
            print 'print my height'
        return item

   def __setattr__(self, key, value):
       if key == 'height':
           print 'set height ok'
       self.__dict__[key] = value

   def test(self):
        print 'test'

a = A()
print a.__dict__
a.height=1
print a.height

输出:
A
{'age': 23, 'name': 'a'}
set height ok
1

当我们执行点号运算时,a.height最终会调用__getattr__,想赋值的时候会调用__setattr__。注意上面:__getattr__只能用来查询不在__dict__系统中的属性,即只拦截类和实例没有的属性,即未在类中定义的。

Python中还有一个__getattribute__特殊方法,它会拦截所有属性的获取。而且__getattribute__要比__getattr__的优先级高,当定义前者之后,后者就不起作用了。

还有一点需要注意,是关于赋值属性,__setattr__会拦截所有属性的赋值,即self.attr=value总会调用self.__setattr__(key,value),所以一定要注意该属性的编写,否则就会出现无限循环。要用self.__dict__[key] = value赋值。

说到这,我们看一个通过__setattr__和__getattr__实现的类的私有属性的方法(我们都知道python并没有提供像Java那样private等方法):


class PrivateExc(Exception):
	pass


class A(object):

	def __setattr__(self, key, value):
		if key not in self.privates:
			raise PrivateExc('{0} not in {1} '.format(key,self))
		else:
			self.__dict__[key] = value

class B(A):
	privates = ['age']

	def __init__(self,age):
		self.age = age

	def __getattr__(self, item):
		return item

b = B(12)
b.age = 30
print b.age
b.name = 'h'

30
Traceback (most recent call last):
  File "D:/Study/Programming/Python/myworks/weiborobot/__init__.py", line 32, in <module>
    b.name = 'h'
  File "D:/Study/Programming/Python/myworks/weiborobot/__init__.py", line 15, in __setattr__
    raise PrivateExc('{0} not in {1} '.format(key,self))
__main__.PrivateExc: name not in <__main__.B object at 0x00000000025A7780> 

 

 

2、类的比较

上面已经说到了,python有很多的内置运算符,我们可以进行重载。比如__lt__,__eq__,__gt__。等等,我们可以通过重载类的这些运算符,来实现我定制的功能,例子:


class Com(object):

    def __init__(self,value):
        self.value = value

    def __eq__(self, other):
        if self.value == other.value:
            return 'they are same'
        else:
            return False

    def __lt__(self, other):
        if self.value < other.value:
            return '{0} is lower than other'.format(self.__class__.__name__)


print Com(1) == Com(2)

好,现在我们实现一个比较有意思的:

现在有一道题:

  a = ?, a<a is True

即a应该等于什么呢,才能是a<a成立?

Python内置的数据结构肯定是实现不聊了,这时就可以用到运算符__lt__了:


class A(object):

	def __lt__(self, other):
		return True

a = A()
print a<a

 

3、__repr__和__str__

除了上面的属性运算符,这两个也是最常见的。他们是以表达式的形式来表示类。他们的共同特点是必须都要返回字符串。不同点是当我们执行print操作时,它会首先尝试__str__和str函数,也就是说如果__str和__repr__都定义了,那么print操作也只是调用__str__,看个例子:

class B(object):

	def __repr__(self):
		return 'B'

	def __str__(self):
		return 'I am B'

b = B()
print b


I am B

而__repr__用途就更广泛啦,用户交互模式下提示回应以及repr函数。此外,如果没有定义__str__,那执行print操作时,就会调用__repr__,但反过来却不行,即如果你在交互模式下敲,如果没定义__repr__,是不会调用__str__的。

因此,__repr__这种方式最常用啦。

--------EOF---------
微信分享/微信扫码阅读