python3--面向对象进阶之内置方法

发布时间:2018-04-19 20:42:07编辑:Run阅读(3611)

    __str__和__repr__

    改变对象的字符串显示__str__, __repr__

    示例

    class List:
        def __init__(self, *args):
            self.l = list(args)
    l = List(1,2,3,4,5)
    print(l)

    执行结果



    打印的是对象的内存地址,如果想要print(对象)显示字符串怎么办?

    定制一个内置的方法__str__即可

    class List:
        def __init__(self, *args):
            self.l = list(args)
    
        def __str__(self):
            return '[%s]' % (','.join([str(i) for i in self.l]))
    
    l = List(1,2,3,4,5)
    
    # 每一个对象, 都有__str__方法
    print(l)
    print(str(l))
    print('%s' % l)

    执行结果

    [1,2,3,4,5]

    [1,2,3,4,5]

    [1,2,3,4,5]


    例2

    class Teacher:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __str__(self):
            return "Teacher's object %s" % self.name
    
        def __repr__(self):
            return 'repr function %s' % self.name
    
    a = Teacher('Sam', 80)
    b = Teacher('Tom', 80)
    print(a)  # 相当于 str(a)的返回值
    print(b)  # 相当于 str(b)的返回值
    print(repr(a))  # 函数 打印repr的返回值
    print(repr(b))  # 函数 打印repr的返回值
    print(a.__repr__())  # repr(obj)的结果和obj.__repr__()是一样的

    执行结果

    Teacher's object Sam

    Teacher's object Tom

    repr function Sam

    repr function Tom

    repr function Sam


    print执行时,是去内部寻找__str__方法,所以print没有输出不了的数据,因为每一个对象都有__str__方法

    当一个对象,没有指定属性和方法时,则打印出内存地址

    class List:
        def __init__(self, *args):
            self.l = list(args)
    
        # def __str__(self):
        #     return '[%s]' % (','.join([str(i) for i in self.l]))
        #
        # def __repr__(self):
        #     return '[%s]' % (','.join([str(i) for i in self.l]))
    
    l = List(1,2,3,4,5)  #注释掉上面的__str__方法,和__repr__方法
    print(str(l))
    print(repr(l))

    执行结果


    重定义内置方法,是为了个性化输出而已

    class List:
        def __init__(self, *args):
            self.l = list(args)
    
        def __str__(self):
            return '[__str__:%s]' % (','.join([str(i) for i in self.l]))
    
        def __repr__(self):
            return '[__repr__:%s]' % (','.join([str(i) for i in self.l]))
    
    l = List(1,2,3,4,5)
    print(str(l))
    print(repr(l))

    执行结果

    [__str__:1,2,3,4,5]

    [__repr__:1,2,3,4,5]


    总结

    当需要使用__str__的场景时找不到__str__就找__repr__

    当需要使用__repr__的场景时找不到__repr__的时候就找父类的__repr__

    __repr__是__str__的备胎


    归一化设计

    blob.png


    format()

    __format__内部调用了这个方法

    class A:
        def __init__(self, name, school, addr):
            self.name = name
            self.school = school
            self.addr = addr
    a = A('Sam', '北大', '徐汇')
    print(format(a))

    执行结果


    对象之所以能用format,是因为object有这个方法

    源码

    def __format__(self, *args, **kwargs): # real signature unknown
        """ default object formatter """
        pass


    自定义格式化输出,format_spec参数必须要有默认参数,否则报错

    class A:
        def __init__(self, name, school, addr):
            self.name = name
            self.school = school
            self.addr = addr
    
        def __format__(self, format_spec):
            return '{obj.name}-{obj.addr}-{obj.school}'.format(obj=self)
    
    a = A('Sam', '北大', '徐汇')
    format_spec = '{obj.name}-{obj.addr}-{obj.school}'
    print(format(a, format_spec))

    执行结果

    Sam-徐汇-北大


    所谓的自定义,就是添加一些个性化的输出,例如

    class A:
        def __init__(self, name, school, addr):
            self.name = name
            self.school = school
            self.addr = addr
    
        def __format__(self, format_spec):
            return format_spec.format(obj=self)
    
    a = A('Sam', '北大', '徐汇')
    b = A('Tom', '清华', '松江')
    # 只需要更改,就可以自定义个性化了
    format_spec = '{obj.name}-{obj.addr}魔都!-{obj.school}很牛!' 
    print(format(a, format_spec))
    print(format(b, format_spec))

    执行结果

    Sam-徐汇魔都!-北大很牛!

    Tom-松江魔都!-清华很牛!


    __str__和__repr__加上__format__的例子

    改变对象的字符串显示__str__, __repr__,自定制格式化字符串__format__

    format_dict = {
        'nat': '{obj.name}-{obj.addr}-{obj.sex}',  # 姓名,地址,性别
        'tna': '{obj.addr}:{obj.name}:{obj.sex}',  # 地址,姓名,性别
        'tan': '{obj.sex}:{obj.name}:{obj.addr}',  # 性别,姓名,地址
    }
    
    class School:
        def __init__(self, name, addr, sex):
            self.name = name
            self.addr = addr
            self.sex = sex
    
        def __repr__(self):
            return '__repr__(%s,%s)' % (self.name, self.addr)
    
        def __str__(self):
            return '__str__(%s,%s)' % (self.name, self.addr)
    
        def __format__(self, format_spec):
            if not format_spec or format_spec not in format_dict:
                format_spec = 'nat'
            fmt = format_dict[format_spec]
            return fmt.format(obj=self)
    s1 = School('张三', '武汉', '男')
    s2 = School('李四', '上海', '男')
    s3 = School('王五', '北京', '男')
    """
    str函数或者print函数----> obj.__str__()
    repr或者交互式解释器----> obj.__repr__()
    如果__str__没有被定义,那么就会使用__repr__来代替输出
    注意:这两方法的返回值必须是字符串,否则抛出异常
    """
    print('from repr:', repr(s1))
    print('from str:', str(s2))
    print('from format:', format(s3))

    执行结果

    from repr: __repr__(张三,武汉)

    from str: __str__(李四,上海)

    from format: 王五-北京-男


    __call__

    对象后面加括号,触发执行

    注:构造方法的执行是由创建对象触发的,即:对象=类名();而对于__call__方法的执行是由对象后加括号触发的,即:对象()或者类()()

    object默认没有__call__方法

    class Teacher():
        def __call__(self, *args, **kwargs):
            print(123)
    t = Teacher()
    t()  # 对象名() 执行__call__
    Teacher()()  # 类名()() 执行__call__

    执行结果

    123

    123


    一个对象是否可调用,完全取决于这个对象对应的类是否实现了__call__

    callable判断一个对象是否可以调用

    class Teacher():
        def __call__(self, *args, **kwargs):
            print('执行__call__方法')
        def call(self):
            print('执行call方法')
    t = Teacher()
    Teacher()()  # 类名()() 执行__call__
    print(callable(t))  # 返回值True或False
    t.call()  # 对象名.方法名() 执行call
    t()  # 对象名() 执行__call__

    执行结果

    执行__call__方法

    True

    执行call方法

    执行__call__方法


    解释:

    call()是人为调用,太麻烦了

    Teacher()()其实是调用了类的__call__方法,有些源码会出现类名()()


    __eq__

    ==实际是调用了__eq__方法,它是判断内存地址是否一致

    class A:pass
    a = A()
    b = A()
    print(a)
    print(b)
    print(a == b)

    执行结果

    <__main__.A object at 0x00000231F5EB22E8>

    <__main__.A object at 0x00000231F5EB2208>

    False


    在来看看__eq__的使用方法

    class B:
        def __init__(self):
            self.a = 1
            self.b = 2
    
        def __eq__(self, other):
            if self.a == other.a and self.b == other.b:
                return True  # 这里改成什么就返回什么
    a = B()
    b = B()
    print(a == b)

    执行结果

    True

    ==是由__eq__的返回值来决定的


    __del__析构方法:析构方法,当对象在内存中被释放时,自动触发执行

    注:此方法一般无需定义,因为python是一门高级语言。程序员在使用时无需关心内存的分配和释放,因为此工作都是交给python解释器来执行,所以,析构函数的调用是由解释器在进行垃圾回收时自动触发执行的。

    在删除一个对象的时候做一些收尾工作

    class A:
        def __init__(self):
            pass
        def __del__(self):
            print('执行我啦')
    
    a = A()
    print('aaa')

    执行结果

    aaa

    执行我啦

    对象执行结束之后,就会执行析构方法


    例2

    class A:
        def __init__(self):
            self.f = open('123.txt', 'w')
        def __del__(self):
            self.f.close()  # 在最后会执行关闭文件的操作
    a = A()
    print('aaa')

    执行结果

    aaa


    __new__构造方法,这个很重要

    实例化的时候

    创建对象的过程 __new__

    init 初始化

    __new__的源码

    @staticmethod # known case of __new__
    def __new__(cls, *more): # known special case of object.__new__
        """ Create and return a new object.  See help(type) for accurate signature. """
        pass

    __new__是一个静态方法,也叫单例模式,先有对象,才能初始化,也就是说__new__要在 __init__之前。


    单例模式就是一个类,只能有一个实例

    错误示例,下面这个例子不属于单例模式

    class A:pass
    a = A()
    b = A()
    print(a)
    print(b)

    执行结果

    <__main__.A object at 0x000001594118E630>

    <__main__.A object at 0x00000159415D2240>

    单例模式就是一个类,只能有一个实例


    真正的单例模式

    class B:
        __instance = None
        def __new__(cls, *args, **kwargs):
            if cls.__instance is None:
                obj = object.__new__(cls)
                cls.__instance = obj
            return cls.__instance
    a = B()
    b = B()
    print(a)
    print(b)

    执行结果

    <__main__.B object at 0x00000204BC4922E8>

    <__main__.B object at 0x00000204BC4922E8>

    内存地址是一样的,单例模式


    例2

    class B:
        __instance = None
        def __new__(cls, *args, **kwargs):
            if cls.__instance is None:
                obj = object.__new__(cls)
                cls.__instance = obj
            return cls.__instance
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
    a = B('Sam', 40)
    b = B('Tom', 28)
    print(a)
    print(b)
    print(a.name)
    print(b.name)  # 打印的都是Tom

    执行结果

    <__main__.B object at 0x0000024BEFC59710>

    <__main__.B object at 0x0000024BEFC59710>

    Tom

    Tom

    相当于重新赋值了,单例模式,只能有一个实例,后面的会覆盖前面的


    item系列

    __getitem__  __setitem__  __delitem__

    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __getitem__(self, item):  # __getitme__只能有一个参数
            print(self.__dict__[item])
    
        def __setitem__(self, key, value):  # __setitme__ 能接收2个参数,一个是等号左边,一个是等号右边
            self.__dict__[key] = value
    
        def __delitem__(self, key):
            print('del obj[key]时,我执行')
            self.__dict__.pop(key)
    
        def __delattr__(self, item):
            print('del obj.key时,我执行')
            self.__dict__.pop(item)
    
    f1 = Foo('sb')
    f1['age'] = 18
    f1['age1'] = 19
    del f1.age1
    del f1['age']
    f1['name'] = 'Sam'
    print(f1.__dict__)

    执行结果

    del obj.key时,我执行

    del obj[key]时,我执行

    {'name': 'Sam'}


关键字