zoukankan      html  css  js  c++  java
  • [ python ] 类中的一些特殊方法

    item系列

    __getitem__(self, item)            对象通过 object[key] 触发
    __setitem__(self, key, value)    对象通过 object[key] = value 触发
    __delitem__(self, key)            对象通过 del object[key] 触发
    class Func:
        def __getitem__(self, item):
            # object[item] 触发
            return self.__dict__[item]
    
        def __setitem__(self, key, value):
            # object[key] = value 触发
            self.__dict__[key] = value
    
        def __delitem__(self, key):
            # del object[key] 触发
            print('delitem: 删除key')
            del self.__dict__[key]
    
        def __delattr__(self, item):
            # del object.item 触发
            print('delattr: 删除key')
            del self.__dict__[item]
    
    
    f = Func()
    f['name'] = 'hkey'  # __setitem__
    f['age'] = 20   # __setitem__
    print(f.name)   # 对象属性原本的调用方式
    print(f['name'])    # __getitem__
    del f['name']   # __delitem__
    print('------')
    del f.age   # __delattr__
    item系列实例

    要注意反射的 __delattr__ 和 __delitem__ 使用不同的方式触发不同的特殊方法。

    class Fib:
        def __getitem__(self, item):
            if isinstance(item, int):
                a, b = 1, 1
                for x in range(item):
                    a, b = b, a+b
                return a
            if isinstance(item, slice):
                start = item.start
                stop = item.stop
                if start is None:
                    start = 0
                a, b = 1, 1
                L = []
                for x in range(stop):
                    if x >= start:
                        L.append(a)
                    a, b = b, a+b
                return L
    
    f = Fib()
    print(f[9])
    print(f[:10])
    
    执行结果:
    55
    [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
    实例:通过item系列像列表一样获取类中斐波那契数列

    __str__ 和 __repr__

    当直接打印一个对象的时候,显示的是一段内存地址。

    In [1]: class Person:
       ...:     def __init__(self, name):
       ...:         self.name = name
    
    In [2]: p = Person('hkey')
    
    In [3]: p
    Out[3]: <__main__.Person at 0x2be9f5c9128>
    
    In [4]: print(p)
    <__main__.Person object at 0x000002BE9F5C9128>

    为了便于更好的理解,我们定义一个__str__方法

    In [1]: class Person:
       ...:     def __init__(self, name):
       ...:         self.name = name
       ...:     def __str__(self):
       ...:         return "hello %s." % self.name
    
    In [2]: p = Person('hkey')
    
    In [3]: p
    Out[3]: <__main__.Person at 0x2858bd468d0>
    
    In [4]: print(p)
    hello hkey.

    定义了__str__方法,直接输出对象还是打印的内存地址,并没有走__str__方法中定义的格式,用print输出信息却调用了__str__方法定义的内容

    In [1]: class Person:
       ...:     def __init__(self, name):
       ...:         self.name = name
       ...:     def __repr__(self):
       ...:         return "hello %s." % self.name
    
    In [2]: p = Person('hkey')
    
    In [3]: p
    Out[3]: hello hkey.
    
    In [4]: print(p)
    hello hkey.

    定义了__repr__方法,不管是直接打印对象还是通过print打印对象,都是走的__repr__中定义的格式。

    总结:

        __repr__ 和 __str__ 这两个方法都是用于显示的,__str__是面向用户的,而__repr__是面向程序员
        
        使用print打印操作会首先尝试__str__和str内置函数,它通常应该返回一个友好的提示
        当__str__不存在的时候,会去找__repr__是否定义,定义则打印__repr__中定义的内容
        
        当我们想在所有环境下都统一显示的话,可以添加__repr__方法。
        当我们想在不同的环境下支持不同的显示,就可以定义__repr__方法和__str__方法,终端交互使用__repr__,用户使用__str__方法

    __new__

    __new__ 是在新式类中出现的方法,它作用在构造函数之前,可以这么理解,在python中存在于类里面的构造方法__init__()负责将类实例化,而在__init__() 启动之前,__new__()决定是否要使用该__init__()方法,因为__new__()可以调用其他类的构造方法或者直接返回别的对象作为本类的实例。

    具体参考:https://www.cnblogs.com/ifantastic/p/3175735.html

    要记住的是:在实例化时,__new__() 先与 __init__() 方法执行.

    通常来讲,新式类开始实例化时,__new__()方法会返回cls(cls代指当前类)的实例,然后该类的__init__()方法作为构造方法会接收这个实例(self)作为自己的一个参数,然后依次传入__new__()方法中接收的位置参数和命名参数。

    In [1]: class Foo:
       ...:     def __init__(self, *args, **kwargs):
       ...:         print('in init function.')    
       ...:     def __new__(cls, *args, **kwargs):    # 实例化时,首先执行__new__方法
       ...:         print('in new function.')
       ...:         return object.__new__(cls, *args, **kwargs)
    
    In [2]: f = Foo()
    in new function.
    in init function.

    对于__new__ 和 __init__ 的个人理解如下:

    首先,我们把一个类比一个生物,使用__new__方法是创建这个生物的本体(实例),当本体(实例)创建好,才能使用__init__来创建这个本体的属性

    一个单例模式来印证上面的描述:

    In [1]: class Foo:
       ...:     def __init__(self, *args, **kwargs):
       ...:         pass
       ...:     def __new__(cls, *args, **kwargs):
       ...:         if not hasattr(cls, '_instance'):
       ...:             cls._instance = object.__new__(cls, *args, **kwargs)
       ...:         return cls._instance
    
    In [2]: one = Foo()
    
    In [3]: two = Foo()
    
    In [4]: id(one)
    Out[4]: 2173030026152
    
    In [5]: id(two)
    Out[5]: 2173030026152
    
    In [6]: print(one == two)
    True
    
    In [7]: print(one is two)
    True

    通过__new__方法,首先创建实例的本体,当第二次实例化时,通过if判断,这个本体已经存在,就直接返回,然后在调用__init__()方法附加属性值

    python3 正常的使用格式:

    class Foo(object):
    
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    
        def __init__(self, name):
            self.name = name
    
    
    f = Foo('hkey')
    print(f.name)

    __del__

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

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

    在 pycharm中当程序执行完,自动触发 __del__ 方法

    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __del__(self):
            print('执行__del__.')
    
    
    f = Foo('hkey')
    
    # 执行结果:
    执行__del__.
    
    __del__ 在类中适合做一些关闭文件句柄等操作。

    __call__

    当对象+()时候[object()] 触发

    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __call__(self, *args, **kwargs):
            print('执行__call__.')
    
    f = Foo('hkey')
    f()
    
    # 执行结果:
    执行__call__.

    __len__

    使用 len(object) 触发

    class Foo:
        def __init__(self, name):
            self.name = name
    
        def __len__(self):
            return 10    # 注意返回结果必须是 int 类型
    
    
    f = Foo('hkey')
    print(len(f))
    
    
    # 执行结果:
    10

    __hash__

    调用 hash(object) 触发

    class Foo:
        def __init__(self):
            self.a = 1
            self.b = 5
    
        def __hash__(self):
            return hash(str(self.a) + str(self.b))
    
    
    f = Foo()
    print(hash(f))
    
    
    # 执行结果:
    2068706206124340336

    __eq__

    当两个对象进行比较时,触发

    class Foo:
        def __init__(self):
            self.a = 1
            self.b = 5
    
        def __eq__(self, other):
            if self.a == other.a and self.b == other.b:
                return True
            return False
    
    a = Foo()
    b = Foo()
    print(a == b)   # 执行这个比较的时候,就调用了 __eq__ 方法
    
    # 执行结果:
    True

    实例练习:纸牌

    from collections import namedtuple
    
    Card = namedtuple('Card', ['rank', 'suit'])
    
    
    class FranchDeck:
        ranks = [x for x in range(2, 11)] + list('JQKA')
        suits = ['红桃', '黑桃', '方块', '梅花']
    
        def __init__(self):
            '''创建一副牌'''
            self._card = [Card(rank, suit) for rank in FranchDeck.ranks for suit in FranchDeck.suits]
    
        def __len__(self):
            '''统计牌数'''
            return len(self._card)
    
        def __getitem__(self, item):
            '''通过object[index]取牌'''
            return self._card[item]
    
        def __setitem__(self, key, value):
            '''调用 shuffle 的时候需要有__setitem__方法'''
            self._card[key] = value
    
    
    f = FranchDeck()
    print(f[:4])    # 按照顺序取牌
    from random import shuffle
    shuffle(f)  # 随机排列
    print(f[:4])    # 切片随机取4张
    
    # 执行结果:
    [Card(rank=2, suit='红桃'), Card(rank=2, suit='黑桃'), Card(rank=2, suit='方块'), Card(rank=2, suit='梅花')]
    [Card(rank=7, suit='红桃'), Card(rank=3, suit='方块'), Card(rank='J', suit='红桃'), Card(rank=5, suit='黑桃')]
    纸牌
  • 相关阅读:
    列行oracle行转列、列转行
    内存销毁Android中Context
    继承指针《深度探索C++对象模型》侯捷译——笔记(一),读后感,附带【插图】
    函数环境lua5.1.4中实现自定义require的loader函数
    修改文件vim 插件:perlsupport的修改和使用
    查询文件[置顶] Android 仿 WP7 布局
    权限用户Exchange 2010 出现用户无权限发邮件,产生的原因是部分用户权限被覆盖或者丢失!
    参数过滤mongodb过滤查询键值
    效果信息[gdc13]《刺客信条3》渲染技术
    破解服务器Android防破解
  • 原文地址:https://www.cnblogs.com/hukey/p/9990081.html
Copyright © 2011-2022 走看看