Python3 CookBook|类与对象(一)

1、改变对象的字符显示

【问题】
你想改变对象实例的打印或显示输出,让它们根据可读性

【解决方案】
要改变一个实例的字符串表示,可以重新定义它的__str__()__repr__()方法。例如:

class Pair:
	def __init__(self,x,y):
		self.x = x
		self.y = y
	
	def __repr__(self):
		return 'Pair({0.x!r},{0.y!r})'.format(self)
	
	def __str__(self):
		return ({0.x!s},{0.y!s}).format(self)
	

__repr__()方法返回一个实例的代码表示形式,通常用来重新构造这个实例。内置repr() 函数返回这个字符串,跟我们使用交互解释器显示的值是一样的。
__str__()方法将实例转化为一个字符串,使用 str() 或 print() 函数会输出这个字符串。

2、自定义字符串格式化

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

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

_format = {
   '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 = _format[code]
       return fmt.format(d=self)


d = Date(2012,12,21)
print(format(d))
#2012--12--21
print(format(d,'mdy'))
#12/21/2012
print('The date is {:ymd}'.format(d))
#The date is 2012--12--21

__format__()方法给python的字符串格式化功能提供一个钩子,这里需要着重强调的是格式化代码的解析工作完全由类自己决定的。因此,格式化代码可以是任何值。

2、调用父类的方法

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

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

class A:
    def spam(self):
        print('A.spam')

class B(A):
    def spam(self):
        print('B.spam')
        super(B, self).spam()
b = B()
b.spam()
#B.spam
#A.spam

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

class A:
    def __init__(self):
        self.x = 0
        
class B(A):
    def __init__(self):
        super(B, self).__init__()
        self.y = 1

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

class Proxy:
    def __init__(self, obj):
        self._obj = obj

    def __getattr__(self, name):
        return getattr(self._obj, name)

    def __setattr__(self, name, value):
        if name.startswith('_'):
            super().__setattr__(name, value)
        else:
            setattr(self._obj, name, value)

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

实际上,大家对于Python中如何正确使用 super()函数普遍知之甚少。有时会看到像下面这样直接调用父类的一个方法:

class Base:
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')

尽管对于大部分代码而言这么做什么问题,但是在更复杂的涉及到多继承的代码中就有可能导致很奇怪的问题发生。比如,考虑如下情况:

class Base:
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        Base.__init__(self)
        print('A.__init__')

class B(Base):
    def __init__(self):
        Base.__init__(self)
        print('B.__init__')


class C(A,B):
    def __init__(self):
        A.__init__(self)
        B.__init__(self)
        print('C.__init__')

# Base.__init__
# A.__init__
# Base.__init__
# B.__init__
# C.__init__

如果你运行这段代码就会发现 Base.__init__()被调用两次,可能两次调用Base.__init__()没什么坏处,但有时候却不是。另一方面,假设你在代码中换成是super(),结果就完美了

class Base:
    def __init__(self):
        print('Base.__init__')

class A(Base):
    def __init__(self):
        super().__init__()
        print('A.__init__')

class B(Base):
    def __init__(self):
        super(B, self).__init__()
        print('B.__init__')


class C(A,B):
    def __init__(self):
        super().__init__()
        print('C.__init__')

# Base.__init__
# B.__init__
# A.__init__
# C.__init__

运行这个新版本后,你会发现每个__init__()方法只会调用一次了。

未完待续 …

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值