定制类

class中有许多前后双下划线的特殊用途函数,比如__slots__限制属性范围,__len__()让 class作用域len()函数。

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

print(Student('Michael'))

打印结果 :

python中实例化子类_python


本来应该打印一串地址什么的,现在这样就更好看了 。

但如果调用 方式 是原来那样

>>> s = Student('Michael')
>>> print(s)
#输出结果:
<__main__.Student object at 0x109afb310>

这就 又不好看了。这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的 区别是__str__()返回用户看到的字符串,而__repr__()返回 程序开发 者看到的字符串,__repr__()是为调试 服务的。
解决办法是再定义一个__repr__(),但通常__str__()__repr__()代码都是一样的,所以有偷懒的写法:

class Student(object):
    def __init__(self, name):
        self.name = name
    def  __str__(self):
        return 'student object(name:%s)' % self.name
    __repr__  = __str__
print(Student('Michael'))
s = Student('zz')
print(s)
#输出结果:
student object(name:Michael)
student object(name:zz)
  • __iter__

一个类如果想要for。。。in循环,就必须写一个__iter__()方法,该方法返回一个迭代对象,然后Python的for循环就不断调用迭代 对象的__next__()方法 拿到循环 的下一个值,直到遇到StopIteration错误退出循环。
例:写一个Fib类,作用于for 循环

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)
  • __getitem__

Fib实例能作用于for 循环 ,但不能以下标访问,想实现就得用到__getitem__()方法了。

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

这样就可以用下标访问了

>>> f = Fib()
>>> f[0]
1
>>> f[1]
1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101

但是还不能切片,想切片就得这样写

class Fib(object):
    def  __getitem__(self, n):
        if isinstance(n, int): #n是索引
            a,  b =  1, 1
            for x in range(n):
                a,  b  = b, a+b
            return a  #注意这里隐含next,每次都有next()
        if  isinstance(n, slice): #还能判断是否切片的,惊了
            start = n.start
            stop = n.stop
            if start is None:   #如果开始是空,就从头开始
                start = 0
            a, b = 1, 1
            L = []
            for x in range(stop):
                if x >= start:
                    L.append(a)     #把循环结果都存下来
                a,b = b,  a + b
            return L

但是还是不能step的操作

>>> f[:10:2]
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

也没有对负数做处理,所以 这是一个不完善的__getitem__()

如果 把对象看成dict,__getitem__()的参数也可能是一个可以作key的object,比如str。
与之对应的是__setitem__()方法,把对象视作list 或 dict来 对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。
通过上面的方法,我们自己定义的类表现的和python自带的list、tuple、dict没什么 区别 ,这完全归功于动态语言的‘鸭子类型’,不需要强制继承某个接口。

  • __getattr__

正常情况下,调用类的方法和属性时,如果不存在就会报错,要避免这个错误,除了可以加上一个 score属性外 ,python还有一个机制,那就是写一个__getattr__()方法 ,动态 返回一个属性。
例:

class Student(object):
    def __init__(self):
        self.name = 'Michael'
    def __getattr__(self, attr): #就是没有要访问这个实例的属性,来这里找,找不到再报错
        if attr == 'score':
            return 99

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

返回函数也可以:

class Student(object):
    def  __getattr__(self, attr):
        if attr == 'age':
            return lambda :25

s  = Student()
print(s.age())

只有在没有找到属性的情况下,才调用__getattr__,已有的属性不会 从__getattr__中查找。
任意调用如s.abc都会返回None,这是因为 我们定义的__getattr__默认返回 就是None。要让class只响应特定的几个属性,我们就要按照 约定,抛出AttributeError的错误:

class Student(object):
    def __getattr__(self, attr):
        if attr == 'age':
            return  lambda :   25
        raise AttributeError('\' student\' object has no attribute \' %s \'' %  attr)

s = Student( )
print(  s.name())

抛出错误 :
AttributeError: ’ student’ object has no attribute ’ name ’

如果要写SDK,给每个URL对应的API都写一个方法,那得累死,而且,API一旦改动,SDK也要改。

__call__
一个对象实例可以有自己的属性和方法,当我们调用实例方法时,我们用instance.method()来调用,能不能直接在实例本身上调用呢,可以。
任何类,只要定义一个__call__()方法,就可以直接对实例 进行调用。

class Student(object):
    def __init__(self, name):
        self.name = name
    def __call__(self):		#就没有说调哪个函数直接实例括号的时候调这个
        print('My name is %s' % self.name)

s = Student('Michael')
s()
输出:
My name is Michael

判断一个对象是对象还是函数,通过判断否可调用,可调用的就是一个Callable对象,比如函数和我们上面定义的带有__call__()的类实例 。

>>> callable(Student()) #能被调用,函数
True
>>> callable(max)		#能被调用,函数
True
>>> callable([1, 2, 3])	#不能被调用,对象
False
>>> callable(None)		#不能被调用,对象
False
>>> callable('str')		#不能被调用,对象
False

当我们需要定义常量时,一个办法是用大写变量通过整数来定义,例如月份