美文网首页
python类的特殊方法(一)

python类的特殊方法(一)

作者: SimonJoe246 | 来源:发表于2018-12-07 22:01 被阅读0次

    类的特殊方法

    类的特殊方法是为了 Python 解释器调用的,你自己并不需要调用它,也就是说没有 myobject.__len__() 这种写法,应该使用 len(myobject)

    在执行 len(myobject) 的时候,如果 myobject 是一个自定义类的对象,那么python自己会去调用由你实现的 __len__ 方法。

    如果 myobject 是一个list, str 或者字节序列(bytearray)的话,那么 Cpython 会抄个近路,__len__ 会直接返回 PyVaryObject 里的 ob_size 属性。 PyVaryObject 是表示内存中长度可变的内置对象的 C语言结构体。直接读取此值会比调用一个方法快得多

    str

    定义一个类

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

    当我们打印这个类的实例时:

    >>> print(Student('Arya'))
    <Student object at 0x00000211DE165DD8>
    

    这样很不方便观看,怎么按我们想要的格式打印出实例的信息呢?这时可以用到__str__方法

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

    这时,再打印:

    >>> s = Student('Arya')
    >>> print(s)
    <class 'Student'> object, name: Arya
    

    可以按我们想要的格式展示了,可以打印实例所属的类,实例的一些属性、方法。

    但是,当我们直接输入此实例时,显示的还是以前的值。

    >>> s
    <Student object at 0x00000211DE165DD8>
    

    这是因为,打印和直接显示变量调用的不是同一个函数,如上,打印调用的是实例的__str__方法,后者调用的是实例内置的__repr__方法。

    当我们对以上两个方法进行重新定义时,便改变了打印和直接显示时显示的内容。

    当重新定义了__str__时,我们可以直接让__repr__与其相等,即可改变直接显示变量的内容。

    当定义中无 __str__, 而 python 又需要调用它时,解释器会用定义的 __repr__ 方法代替

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

    getattr

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

    定义类的几种属性,当我们尝试调用时:

    >>> s = Student('Arya', '007', 95)
    >>> s.name
    'Arya'
    >>> s.id
    '007'
    >>> s.score
    95
    >>> s.city
    Traceback (most recent call last):
      File "<input>", line 1, in <module>
    AttributeError: 'Student' object has no attribute 'city'
    

    当调用不存在的属性时,会报错,要想不报错,返回指定的内容,就可以用__getattr__。注意,只有访问不存在的属性时,才会调用这个函数。

    还有一个类似的特殊方法__getattribute__,定义了这个方法以后,当调用实例属性时,无论其存不存在,都将执行这个方法。如果同时定义了__getattribute____getattr__,那么调用属性时后者不会被被调用,除非在前者中调用了后者。

    class Student(object):
    
        def __init__(self, name, id, score):
            self.name = name
            self.id = id
            self.score = score
    
        def __getattribute__(self, item):
            return object.__getattribute__(self, item)
    

    另外,在__getattribute__的定义中,返回值不能是__self.__dict__[item],因为如果这样定义,还会调用这个__getattribute__函数,会形成无限递归,形成死循环。

    class Student(object):
    
        def __init__(self, name, id, score):
            self.name = name
            self.id = id
            self.score = score
    
        def __getattr__(self, item):
            if item == 'city':
                return 'Beijing'
    

    这时,我们再调用

    >>> s = Student('Arya', '007', 95)
    >>> s.city
    'Beijing'
    >>> s.sex
    

    就会返回指定的内容,当我们再调用未指定的属性时,会返回None,要想只返回特定的内容,就要抛出AttributeError

    class Student(object):
    
        def __init__(self, name, id, score):
            self.name = name
            self.id = id
            self.score = score
    
        def __getattr__(self, item):
            if item == 'city':
                return 'Beijing'
            raise AttributeError('Student object has no attribute %s' %(self.__class__, item))
    

    setattr

    如果我在程序运行期间想为其增加某种属性怎么办,可以使用__setattr__:

    class Student(object):
    
        def __init__(self, name, id, score):
            self.name = name
            self.id = id
            self.score = score
    
        def __setattr__(self, key, value):
            self.__dict__[key] = value
    

    这时我们就可以使用s.key = value了:

    >>> s = Student('Arya', '007', 95)
    >>> s.sex = 'girl'
    >>> s.sex
    'girl'
    

    增加的属性是实例属性,存储在实例的__dict__中。

    call

    如果想让创建的实例可以当作函数调用,可以使用__call__方法:

    class average(object):
    
        def __init__(self, *args):
            self.count = 0
            self.add = 0
            for x in args:
                self.count += 1
                self.add += x
    
        def __call__(self):
            return self.add/self.count
    

    如此:

    >>> s = average(10, 9, 8, 7)
    >>> s.count
    4
    >>> s.add
    34
    >>> s()
    8.5
    

    还可以在__call__定义中加参数

    getitem

    class Fib(object):
        """
        >>> Fib()[0]
        1
        >>> Fib()[1]
        1
        >>> Fib()[2]
        2
        >>> Fib()[3]
        3
        >>> Fib()[4]
        5
        >>> Fib()[10]
        89
        """
        def __getitem__(self, n):
            a, b = 1, 1
            for x in range(n):
                a, b = b, a+b
            return a
    
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    

    如果想实现按照下标访问元素的方法就要使用 __getitem__ 方法,

    另外,需要注意的是,传入的参数可能是索引数字,也可能是切片,如果是未对切片作相应处理会报错:

    class Fib(object):
        """
        >>> Fib()[0]
        1
        >>> Fib()[1]
        1
        >>> Fib()[2]
        2
        >>> Fib()[3]
        3
        >>> Fib()[4]
        5
        >>> Fib()[10]
        89
        >>> Fib()[0:5]
        [1, 1, 2, 3, 5]
        """
        def __getitem__(self, n):
            if isinstance(n, int):
                a, b = 1, 1
                for x in range(n):
                    a, b = b, a+b
                return a
            if isinstance(n, slice):
                start = n.start
                stop = n.stop
                if start == None:
                    start = 0
                L = []
                a, b = 1, 1
                for x in range(stop):
                    L.append(a)
                    a, b = b, a+b
                L = L[start:stop]
                return L
            
        
    if __name__ == '__main__':
        import doctest
        doctest.testmod()
    

    同时,把对象视为 dict , __getitem__ 的参数也可能是一个可以作为 key 的对象,比如 str

    class Student(object):
        def __init__(self, name, gender):
            self.name = name
            self.gender = gender
    
        def __getitem__(self, key):
            return self.__dict__[key]
            
    >>> s = Student('Arya', 'girl')
    >>> s.name
    'Arya'
    >>> s['name']
    'Arya'
    
    
    - tips: `__getattr__` 和 `__getattribute__` 是用来访问实例对象属性的, `__getitem__`是可以通过索引访问对应值,也可以将对象视为 dict, 访问其 key
    
    与其对应的是 `__setitem__` ,可以给对象新增 key 。
    

    相关文章

      网友评论

          本文标题:python类的特殊方法(一)

          本文链接:https://www.haomeiwen.com/subject/tfzxhqtx.html