python 基础--类

面向对象编程–python 类

本文学习自廖雪峰老师的教程,如果想要系统学习请点廖雪峰的python教程

类特殊的系统变量

_slots_

_slots_ = (‘name’, ‘age’) # 用tuple定义允许绑定的属性名称,注意此限制对子类不影响

class Student():
    __slots__ = ('score','name')

@property

为了方便的调用属性,python提供了@property装饰器

class Student():
    @property  # property修饰的函数可以使用属性,并且property会创建@score.setter进行属性赋值,如果不适用@score.setter属性则表示只读
    def score(self):
        return self._score  # 注意内部将变量添加_或者__表示私有变量,不允许访问,但是__的其实可以通过_Student__score和_score访问
    @score.setter
    def score(self,value):
        if not isinstance(value,int):
            raise ValueError('score must be an integer!')
        if value<0 or value>100 :
            raise  ValueError('value must between 0~100')
        self._score = value

注意:如果不把score变量声明为_score, 那么score(self, value)为了得出最后的self.score = value,会对self.score这个函数进行循环调用。如果声明了, self._score = value,那么很明显self._score必定是变量,就会一次性得出结果
举一个例子:利用@property给一个Screen对象加上width和height属性,以及一个只读属性resolution

class Screen(object):
    @property
    def width(self):
        return self._width
    @width.setter
    def width(self,width):
        self._width = width
    @property
    def height(self):
        return self._height
    @height.setter
    def height(self, height):
        self._height = height
    @property
    def resolution(self):
        return self._width*self._height

_str_

__str__用于自定义打印一个实例,代码如下

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

>>>print(Student('Micyle'))
打印出来后
student name:Micyle
但如果我们直接敲变量,还是之前的变量地址

>>>s = Student('Micyle')
>>>s
<__main__.Student at 0x10b0996d8>

这是因为直接敲变量调用的是__repr__而不是__str__,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__

_iter_

如果一个类想被用于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 > 1000:
            raise StopIteration()
        return self.a
for i in Fib():
    print(i)

_getitem_

Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list来使用还是不行,比如,取第5个元素:
>>>Fib()[5] 会报错,这里我们可以使用__getitem__

class Fib(object):
    def __getitem__(self,n):
        a,b = 0,1
        for i in range(n):
            a,b = b,a+b
        return a # 这里其实返回的一个a的列表

但是如果我们使用切片方法,会报错
Fib()[5:10] 这是因为__getitem__()传入的参数可能是一个int,也可能是一个切片对象slice,所以要做判断:

class Fib(object):
    def __getitem__(self,n): # getitem将返回的值组成一个列表的形式,而返回的L也是一个列表,因而是列表中的元素还是列表
        if isinstance(n,int):
            a,b = 1,1
            for i in range(n):
                a,b = b,a+b
            return a
        elif isinstance(n,slice):
            a,b = 1,1
            start = n.start
            stop = n.stop
            if n.start is None:
                start = 0
            l = []
            for i in range(n.stop):
                if i>=start:
                    l.append(a)
                a,b = b,a+b
            return l

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

_getattr_

当我们调用类的方法或属性时,如果不存在,就会报错。比如定义Student类,如果没有定义score属性,调用就会报错

class Student():
    def __init__(self):
        self.name = 'Micyle'
s = Student()
s.score

要避免这个错误,除了可以加上一个score属性外,Python还有另一个机制,那就是写一个__getattr__()方法,动态返回一个属性。

class Student():
    def __init__(self):
        self.name = 'Micyle'
    def __getattr__(self,attr):
        if attr=='score':
            return 99

当调用不存在的属性时,比如score,Python解释器会试图调用__getattr__(self, ‘score’)来尝试获得属性,这样,我们就有机会返回score的值:

class Student():
    def __init__(self):
        self.name = 'Micyle'
    def __getattr__(self,attr):
        if attr=='score':
            return lambda :1+2
a = Student()
a.score()

只需调用a.score()可以返回函数运算值
注意:只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找。
此外,注意到任意调用如s.abc都会返回None,这是因为我们定义的__getattr__默认返回就是None。要让class只响应特定的几个属性,我们就要按照约定,抛出AttributeError的错误:

class Student():
    def __init__(self):
        self.name = 'Micyle'
    def __getattr__(self,attr):
        if attr=='score':
            return lambda :1+2
        raise AttributeError('student object has no %s'%attr)

举一个很好的例子应用getattr方法,输出给每个URL对应的API

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__

通过调用Chain('xxx').status.user.timeline.list可以得到如下结果
xxx/status/user/timeline/list
部分api的生成包含了用户名,如果可以采用以下方式调用就比较方便
Chain().users('michael').repos
可以用以下代码实现

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
    def users(self,str):
        return Chain('%s/%s'%(self._path,'/users/' + str))
    __repr__ = __str__

_call_

实例有自己的属性和方法,通过instance.method()来调用,同时也可以直接通过实例调用,只需要定义一个__call__()方法

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

通过__call__()方法就只可以直接调用实例方法
a = Student('song')
a()
另外,函数与对象没有根本的区别,我们可以通过callable来判断对象能否被调用
callable(a) 结果为True

猜你喜欢

转载自blog.csdn.net/weixin_40548136/article/details/86087307