美文网首页大数据 爬虫Python AI Sql
Python面向对象之魔术方法

Python面向对象之魔术方法

作者: 919b0c54458f | 来源:发表于2019-03-17 15:46 被阅读5次

    str
    改变对象的字符串显示。可以理解为使用print函数打印一个对象时,会自动调用对象的str方法

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

    定义对象的字符串表示

     def __str__(self):
     return self.name
     
    s1 = Student('张三', 24)
    print(s1) # 会调用s1的__str__方法
    __repr__
    在python解释器环境下,会默认显示对象的repr表示。
    >>> class Student:
    ... def __init__(self, name, age):
    ... self.name = name
    ... self.age = age
    ... def __repr__(self):
    ... return self.name
    ... 
    >>> s1 = Student('张三', 24)
    >>> s1
    张三
    

    总结:
    str函数或者print函数调用的是obj.str()
    repr函数或者交互式解释器调用的是obj.repr()
    注意:
    如果str没有被定义,那么就会使用repr来代替输出。
    strrepr方法的返回值都必须是字符串。

    __format__
    class Student:
     def __init__(self, name, age):
     self.name = name
     self.age = age
     __format_dict = {
     'n-a': '名字是:{obj.name}-年龄是:{obj.age}', # 名字是:lqz-年龄是:18
     'n:a': '名字是:{obj.name}:年龄是:{obj.age}', # 名字是:lqz:年龄是:18
     'n/a': '名字是:{obj.name}/年龄是:{obj.age}', # 名字是:/年龄是:18
     }
     def __format__(self, format_spec):
     if not format_spec or format_spec not in self.__format_dict:
     format_spec = 'n-a'
     fmt = self.__format_dict[format_spec]
     print(fmt) #{obj.name}:{obj.age}
     return fmt.format(obj=self)
    s1 = Student('lqz', 24)
    ret = format(s1, 'n/a')
    print(ret) # lqz/24
    

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

    class A:
     def __del__(self):
     print('删除了...')
    a = A()
    print(a) # <__main__.A object at 0x10164fb00>
    del a # 删除了...
    print(a) # NameError: name 'a' is not defined
    

    dictslots
    Python中的类,都会从object里继承一个dict属性,这个属性中存放着类的属性和方法对应的键值对。一个类实例化之后,这个类的实例也具有这么一个dict属性。但是二者并不相同。

    class A:
     some = 1
     def __init__(self, num):
     self.num = num
    a = A(10)
    print(a.__dict__) # {'num': 10}
    a.age = 10
    print(a.__dict__) # {'num': 10, 'age': 10}
    

    从上面的例子可以看出来,实例只保存实例的属性和方法,类的属性和方法它是不保存的。正是由于类和实例有dict属性,所以类和实例可以在运行过程动态添加属性和方法。
    但是由于每实例化一个类都要分配一个dict变量,容易浪费内存。因此在Python中有一个内置的slots属性。当一个类设置了slots属性后,这个类的dict属性就不存在了(同理,该类的实例也不存在dict属性),如此一来,设置了slots属性的类的属性,只能是预先设定好的。
    当你定义slots后,slots就会为实例使用一种更加紧凑的内部表示。实例通过一个很小的固定大小的小型数组来构建的,而不是为每个实例都定义一个dict字典,在slots中列出的属性名在内部被映射到这个数组的特定索引上。使用slots带来的副作用是我们没有办法给实例添加任何新的属性了。
    注意:尽管slots看起来是个非常有用的特性,但是除非你十分确切的知道要使用它,否则尽量不要使用它。比如定义了slots属性的类就不支持多继承。slots通常都是作为一种优化工具来使用。--摘自《Python Cookbook》8.4

    class A:
     __slots__ = ['name', 'age']
     
    a1 = A()
    # print(a1.__dict__) # AttributeError: 'A' object has no attribute '__dict__'
    a1.name = '张三'
    a1.age = 24
    # a1.hobby = '泡妞' # AttributeError: 'A' object has no attribute 'hobby'
    print(a1.__slots__)
    

    注意事项:
    slots的很多特性都依赖于普通的基于字典的实现。
    另外,定义了slots后的类不再 支持一些普通类特性了,比如多继承。大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义slots,比如在程序中需要创建某个类的几百万个实例对象 。
    关于slots的一个常见误区是它可以作为一个封装工具来防止用户给实例增加新的属性。尽管使用slots可以达到这样的目的,但是这个并不是它的初衷。它更多的是用来作为一个内存优化工具。
    item系列

    class Foo:
     def __init__(self, name):
     self.name = name
     def __getitem__(self, item):
     print(self.__dict__[item])
     def __setitem__(self, key, value):
     print('obj[key]=lqz赋值时,执行我')
     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')
    print(f1.__dict__)
    f1['age'] = 18
    f1.hobby = '泡妞'
    del f1.hobby
    del f1['age']
    f1['name'] = 'lqz'
    print(f1.__dict__)
    

    init
    使用Python写面向对象的代码的时候我们都会习惯性写一个 init 方法,init 方法通常用在初始化一个类实例的时候。例如:

    class Person:
     def __init__(self, name, age):
     self.name = name
     self.age = age
     def __str__(self):
     return '<Person: {}({})>'.format(self.name, self.age)
    p1 = Person('张三', 24)
    print(p1)
    

    上面是init最普通的用法了。但是init其实不是实例化一个类的时候第一个被调用的方法。当使用 Persion(name, age) 来实例化一个类时,最先被调用的方法其实是 new 方法。

    Python学习交流群:556370268,有资源共享,经验分享,这里是python学习者聚集地,欢迎喜欢Python的小伙伴!

    new
    其实init是在类实例被创建之后调用的,它完成的是类实例的初始化操作,而 new方法正是创建这个类实例的方法

    class Person:
     def __new__(cls, *args, **kwargs):
     print('调用__new__,创建类实例')
     return super().__new__(Person)
     def __init__(self, name, age):
     print('调用__init__,初始化实例')
     self.name = name
     self.age = age
     def __str__(self):
     return '<Person: {}({})>'.format(self.name, self.age)
    p1 = Person('张三', 24)
    print(p1)
    

    输出:

    调用__new__,创建类实例
    调用__init__,初始化实例
    <Person: 张三(24)>
    

    new方法在类定义中不是必须写的,如果没定义的话默认会调用object.new去创建一个对象(因为创建类的时候默认继承的就是object)。
    如果我们在类中定义了new方法,就是重写了默认的new方法,我们可以借此自定义创建对象的行为。
    举个例子:
    重写类的new方法来实现单例模式。

    class Singleton:
     # 重写__new__方法,实现每一次实例化的时候,返回同一个instance对象
     def __new__(cls, *args, **kw):
     if not hasattr(cls, '_instance'):
     cls._instance = super().__new__(Singleton)
     return cls._instance
     def __init__(self, name, age):
     self.name = name
     self.age = age
    s1 = Singleton('张三', 24)
    s2 = Singleton('李四', 20)
    print(s1, s2) # 这两实例都一样
    print(s1.name, s2.name)
    

    call
    call 方法的执行是由对象后加括号触发的,即:对象()。拥有此方法的对象可以像函数一样被调用。

    class Person:
     def __init__(self, name, age):
     self.name = name
     self.age = age
     def __call__(self, *args, **kwargs):
     print('调用对象的__call__方法')
    a = Person('张三', 24) # 类Person可调用
    a() # 对象a可以调用
    

    注意:
    newinitcall等方法都不是必须写的。
    doc
    定义类的描述信息。注意该信息无法被继承。

    class A:
     """我是A类的描述信息"""
     pass
    print(A.__doc__)
    

    iternext
    如果一个对象拥有了iternext方法,那这个对象就是可迭代对象

    class A:
     def __init__(self, start, stop=None):
     if not stop:
     start, stop = 0, start
     self.start = start
     self.stop = stop
     def __iter__(self):
     return self
     def __next__(self):
     if self.start >= self.stop:
     raise StopIteration
     n = self.start
     self.start += 1
     return n
    a = A(1, 5)
    from collections import Iterator
    print(isinstance(a, Iterator))
    for i in A(1, 5):
     print(i)
    for i in A(5):
     print(i)
    aaa=A(1)
    print(next(aaa))
    print(next(aaa)) #抛异常
    

    enterexit
    一个对象如果实现了enter和_exit方法,那么这个对象就支持上下文管理协议,即with语句

    class A:
     def __enter__(self):
     print('进入with语句块时执行此方法,此方法如果有返回值会赋值给as声明的变量')
     return 'oo'
     def __exit__(self, exc_type, exc_val, exc_tb):
     print('退出with代码块时执行此方法')
     print('1', exc_type)
     print('2', exc_val)
     print('3', exc_tb)
    with A() as f:
     print('进入with语句块')
     # with语句中代码块出现异常,则with后的代码都无法执行。
     # raise AttributeError('sb')
     print(f) #f打印出oo
    print('嘿嘿嘿')
    

    上下文管理协议适用于那些进入和退出之后自动执行一些代码的场景,比如文件、网络连接、数据库连接或使用锁的编码场景等。
    len

    拥有__len__方法的对象支持len(obj)操作。
    class A:
     def __init__(self):
     self.x = 1
     self.y = 2
     def __len__(self):
     return len(self.__dict__)
    a = A()
    print(len(a))
    

    hash
    拥有hash方法的对象支持hash(obj)操作。

    class A:
     def __init__(self):
     self.x = 1
     self.x = 2
     def __hash__(self):
     return hash(str(self.x) + str(self.x))
    a = A()
    print(hash(a))
    

    eq
    拥有eq方法的对象支持相等的比较操作

    class A:
     def __init__(self,x,y):
     self.x = x
     self.y = y
     def __eq__(self,obj):
     # 打印出比较的第二个对象的x值
     print(obj.x)
     if self.x +self.y == obj.x+obj.y:
     return True
     else:
     return False
    a = A(1,2)
    b = A(2,1)
    print(a == b)
    

    相关文章

      网友评论

        本文标题:Python面向对象之魔术方法

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