Python:面向对象编程3 定制类(有更新)

Python:面向对象编程3  定制类(有更新)

 

⚠️本文主要内容为对Data model相关知识点的提取学习记录。(内容来自文档和部分网页教程案例)

⚠️:这个连接指向《流畅的python》的学习笔记。https://www.cnblogs.com/chentianwei/p/11975199.html

 

特殊方法名称Special method names

 

一个类可以通过定义具有特殊名称的方法来实现由特殊语法所引发的特定操作 (例如算术运算或下标与切片)。

This is Python’s approach to operator overloading, allowing classes to define their own behavior with respect to language operators.

就语言操作方面而言,这就是PYthon实现操作符overloading, 或允许里定义自身的行为的方式approach。

例如:

如果,MyClass类定义了方法__getitem(),MyClass的一个实例是x, x[i]大体等同type(x).__getitem))(x, i). 

 

 

下面的方法,为程序员自定义某些功能提供了接口,利用这些方法,可以更丰富的实现对应的功能。

  • 基本定制方法

  • 模拟可调用对象Emulating callable object

  • 模拟容器类型

  • 自定义访问属性 


 

Basic customization 基本定制方法

 

object.__init__(self[, ...])

object.__repr__(self)

由 repr() 内置函数调用以输出一个对象的“官方”字符串表示。此方法通常被用于调试,因此确保其表示的内容包含丰富信息且无歧义是很重要的。

object.__str__(self)

通过 str(object) 以及内置函数 format() 和 print() 调用以生成一个对象的“非正式”或格式良好的字符串表示。返回值必须为一个 字符串 对象。

 

上面三个特殊方法的使用例子:

class Student(object):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return 'Student object (name: %s)' %(self.name)

print(Student('Tom'))

#打印出自定义的信息输出Student object (name: Tom)

 

但是__str__必须配合print()才行, 如果不用print()👇:

>>> s = Student('Michael')
>>> s
<__main__.Student object at 0x109afb310>

 

此时可以使用__repr__方法,因为和__str__代码一样,所以可以指向__str__

class Student(object):
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return 'Student object (name=%s)' % self.name
    __repr__ = __str__

 

 

object.__iter__(self)

此方法在需要为容器创建迭代器时被调用。此方法应该返回一个新的迭代器对象,它能够逐个迭代容器中的所有对象。

如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。

例子:斐波那契数列:

class Fib(object):
    def __init__(self):
        self.a, self.b = 0, 1

    def __iter__(self):
        return self        # 实例本身就是迭代对象,故返回自己

    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        if self.a > 100000:   # 退出循环的条件
            raise StopIteration()
        return self.a

for n in Fib():
    print(n)

 

 


 

模拟泛型类型

通过定义一个特殊的方法,可以实现由 PEP 484 所规定的泛型类语法the generic class syntax

 

模拟可调用对象 Emulating callable objects

object.__call__(self[, args...])

把一个实例对象看成一个函数,并调用它,这个方法会被调用。

解释:

Python中一切都可以看做对象,函数也是对象。通过__call__,也可以反过来,把对象看成是函数。对象后加上(),就执行一个方法,即__call__方法。

因此x(arg1, ...)可以看成是x.__call__(arg1, ...)的语法糖,简便写法。

class Student(object):
    def __init__(self, name):
        self.name = name

    def __call__(self):
        print('My name is %s.' % self.name)

s = Student("Tom")
s()
#会调用__call__

 

 

如何判断一个对象是否是可调用对象?

使用内置函数callable(object),返回bool值。

 

 


 

模拟容器类型

可以定义以下方法来实现容器对象。

容器通常是序列list,tuple, 或者映射dict,但也存在其他容器。

通过这些方法,我们可以对自定义的类进行类似Python自带的list,dict类型一样的增删操作。无需强制继承某个接口。

 

object.__getitem__(selfname) 

调用此方法以实现 self[key] 的求值。对于序列类型,key为int, slice。

 

例子,f[89]中的中括号,可以调用__getitem__()。

class Fib(object):
    def __getitem__(self, n):
        a, b = 1 , 1
        for x in range(n):
            a, b = b, a + b
        return n

f = Fib()
print(f[89])

 

 


 

自定义访问属性

 

针对类实例的操作。

调用一个类的对象的方法/属性:

  1. 到对象的dict对象中去查找,dict是类实例的一个dict类型的储存空间。没找到则:
  2. 在类属性中查找。没找到则:
  3. 看类是否有__getattr__()方法,如果有则调用该方法,相当于一种补救措施。

属性的赋值和删除会更新实例的dict,但不会更新它的类的dict。如果类有__setattr__(), __delattr__()方法,则调用该方法,并且不再更新实例的dict。

 

object.__getattr__(selfname)

当默认属性访问因引发 AttributeError 而失败时被调用。(翻译的太差)

当默认属性访问失败时(会引发AttributeError),实例对象自动的调用__getattr__方法。

此方法应该返回(找到的)属性值或是引发一个 AttributeError 异常。

例子:

class Student(object):
    def __init__(self, name):
        self.name = name

#    def __getattr__(self, attr):
#        if attr == "score":
#           return 99

s = Student("Tom")
print(s.name)
print(s.score)

会报错AttributeError: 'Student' object has no attribute 'score'。

去掉注释:实例对象自动调用__getattr__()。

但是,__getattr__的功能还有抛出错误异常,这是为了给程序反馈一个结果,因此上面的注释部分还要加上一行:

raise AttributeError('\'Student\' object has no attribute \'%s\'' % attr)

  

通过__getattr__, 一个类的所有属性和方法调用就全部动态化处理了,即原先没有的属性/方法,用__getattr__去做处理。

 

下面一个例子,动态添加APi的URL地址:

class Chain(object):

    def __init__(self, path=""):
        self._path = path

    def __getattr__(self, path):
        return Chain('%s/%s' % (self._path, path))

    def __str__(self):
        return self._path

    __repr__ = __str__

print(Chain().status.user.timeline.list) 

 最后一行,利用__getattr__, 生成新的实例。

 

⚠️Ruby有ghost method类似于__getattr__,即BasicObject#method_missing方法。

class Chain
  attr_accessor :path
  
  def initialize(path="")
    @path = path
  end

  def method_missing(path)
    Chain.new("#{@path}/#{path}")
  end
end

s = Chain.new.hello.tom.ok
p s.path

 

   

__setattr(self, name, value)__

此方法在一个属性被尝试赋值时被调用。这个调用会取代正常机制(即将值保存到实例字典dict)。 name 为属性名称, value 为要赋给属性的值。

object.__delattr__(selfname

删除而非赋值


 

 

posted @ 2019-11-11 16:07  Mr-chen  阅读(292)  评论(0)    收藏  举报