zoukankan      html  css  js  c++  java
  • 快速了解Python的定制类

    多重继承

    class Student(man,oldman):

      pass

    可以继承多个父类,拥有他们的方法,如果有父类有相同的方法,哪个在前用哪个

    定制类

    看到类似__slots__这种形如 __xxx__的变量或函数名就要注意,这些在python中是有特殊用途的

    我们已经知道了__slots__的用法,用__len__()方法我们也知道是为了能让class作用于len()函数

    __str__

    我们先定义一个Student类,打印一个实例

    class Studentc():
        def __init__(self,name):
            self.__name = name
    print(Studentc('cc'))
    --> <__main__.Studentc object at 0x0000000000B48B00>

    打印出来一坨不好看,我们定义一下__str__()方法,返回一个好看的字符串

    class Studentc():
        def __init__(self,name):
            self.__name = name
        def __str__(self):
            return 'Studentc object (name : %s)'% self.__name
    print(Studentc('cc')) #  Studentc object (name : cc)

    这里return 不用敲print

    但是直接敲变量在shell下运行,打印出来的还是不好看

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

    这是因为直接显示变量调用的不是__str__(),而是__repr__(),两者的区别就是__str__()返回用户看到的字符串,而__repr__返回程序开发者看到的字符串,也就是说__repr__()是为调试服务的

    解决办法是再定义一个__repr__()。但是通常两个代码是一样的,可以偷懒

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

    __iter__

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

     
    class Fib():
        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
            else:
                return self.__a
    for x in Fib():
        print(x)
     

    __getitem__

    Fib实例虽然能作用于for循环,看起来和list有点像,但是,把它当成list使用还是不行比如取第五个元素

     
    class Suv():
        def __getitem__(self,n):
            a,b = 0,1
            for x in range(n):
                a,b = b,a+b
            return a
    print(Suv()[3])
     

    list还有切片功能,这里不能用,因为我们不知道传进来的是什么需要做一个判断

    isinstance(n,slice) slice是切片的类型。还有负数等没有处理真想完全实现的话需要添加很多

    __getattr__

    正常情况下,当我们调用类的方法或属性时,如果不存在,就会报错

    避免这种情况,除了可以加上一个score属性外,python还有另一个机智,那就是写一个__getattr__()方法,动态返回一个属性

     
    class Su():
        def __init__(self):
            self.name = 'cc'
        def __getattr__(self, item):
            if item == 'score':
                return '99'
    print(Su().score)
     

    当调用的属性不存在时,比如score,python会试图调用__getattr__(self,'score')来尝试获得属性,这样我们就有机会返回score的值

    返回函数也是可以的

     
    class Student():
        def __init__(self):
            self.name = 'cc'
        def __getattr__(self, item):
            if item == 'age':
                return lambda :25
    print(Student().age())
     

    返回一个函数,调用的时候修改成Student().age()

    注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如name,不会在__getattr__中查找

    此外,如果没对未知的属性做处理,if判断输出的话。会返回None,要让class只响应特定的几个属性,我们就按照约定抛出AttributeError的错误

     
    class Student():
        def __init__(self):
            self.name = 'cc'
        def __getattr__(self, item):
            if item == 'age':
                return lambda :25
            raise AttributeError('no have %s'%item)
    print(Student().ac())
     

    这实际上可以吧一个类的所有属性和方法调用全部动态化处理了,不需要任何特殊手段

    这种完全动态调用的特性与什么实际作用呢?作用就是可以针对完全动态的情况作调用

    举个例子

    现在很多网站都搞rest api,比如新浪微博,豆瓣 调用api的url类似 

    http://api.server/user/friends

    如果要写sdk,给诶给url对应的api写一个方法,那得累死,而且一旦api改动,sdk也要改

    利用完全动态的__getaattr__我们可以写一个链式调用

     
    class Chain():
        def __init__(self,ab=""):
            self._path =ab
        def __getattr__(self, path):
            print('self._path = %s ,path = %s'%(self._path,path))
            return Chain('%s/%s' % (self._path,path))
        def __str__(self):
            return self._path
        __reper__ = __str__
    c = Chain().status.user.time.list
    print(c)
     
    1
    2
    3
    4
    5
    self._path =  ,path = status
    self._path = /status ,path = user
    self._path = /status/user ,path = time
    self._path = /status/user/time ,path = list
    /status/user/time/list

    属性都不存在,每一次都调用__getattr__方法,返回当前self._path + 属性名,并被_path获得,不断累加直到没有属性

    打印返回self._path 的值

    __call__

    一个对象实例可以与自己的属性和方法,当我们调用实例方法时,我们会用instance.methond()来调用。能不能直接在实例本身上调用呢,在python答案是肯定的

    任何类,只需要定义一个__call__()方法,就可以直接对实例进行调用

     
    class Students():
        def __init__(self,name):
            self.name = name
        def __call__(self):
            print('myname is %s'% self.name)
    s1 = Students('s1')
    s1()
     

    __call__()还可以定义参数。对实例进行直接调用就好比对一个函数调用一样,所以你完全可以把对象看出函数,把函数看成对象,因为两者之间本来就没有什么根本的区别

    如果你把对象看成函数,那么函数本身其实也可以在运行期间动态创建出来的,这么一来,我们就模糊了对象和函数的界限。

    那么如何判断一个变量是对象还是函数呢?其实我们只需要判断一个对象是否能被调用,能被调用的对象就是一个Callable对象,比如函数和我们上面定义的__call__()的类实例

    print(callable(Student))
    print(callable([1,2,34]))
    print(callable(None))
    print(callable('str'))
  • 相关阅读:
    腾讯云通信服务端返回签名
    synchronized同步语句块
    synchronized同步方法
    springjdbc的批量操作
    yield方法
    暂停线程
    【jdk源码学习】HashMap
    diamond types are not supported at this language level
    【java基础系列】一、常用命令行
    Socket通信综合示例
  • 原文地址:https://www.cnblogs.com/ltb6w/p/9064528.html
Copyright © 2011-2022 走看看