zoukankan      html  css  js  c++  java
  • 魔法函数

    1.什么是魔法函数

    在python中,有的名称以双下划线开头同时以双下划线结尾这种形式,
    我们只知道它是python自己定义的,同时我们也不应该去定义类似的函数。

    我们将“__init__”这种形式的函数成为魔法函数。"__init__"是构造器,用来初始化对象。
    魔法函数不需要去显式的调用,同时魔法函数不能自定义。
    魔法函数会被python隐式的去被调用。

    举个简单的例子:
    现在我们需要遍历对象中的属性,传统做法是取属性然后去遍历。

    class Person(object):
        def __init__(self,p_list):
            self.p_list = p_list
    
    person = Person(["ming","ke","mao"])
    
    for em in person.p_list:
        print(em)
    
    我们可以使用__getitem__来更简单的实现。
    class NewPerson(object):
        def __init__(self,p_list):
            self.p_list = p_list
    
        def __getitem__(self, item):  
            return self.p_list[item]
    
    newPerson = NewPerson(["ming","kebi","mao"])
    #这里就不需要取属性了。
    for em in newPerson:
        print(em)

    我们可以通过不添加__getitem__,来知道__getitem__是将对象转换成可迭代对象。
    #'NewPerson' object is not iterable
    在python中,只有可迭代对象才可以进行遍历,而可迭代对象必须拥有item()或者__item__方法。
    当python在进行循环的时候,会去看对象是否由item()方法,如果没有会去看是否与__getitem__魔法函数,否则就不能迭代。

    2.python魔法函数对python语法的影响


    魔法函数会影响python语法本身。

    示例一:

    class NewPerson(object):
        def __init__(self,p_list):
            self.p_list = p_list
    
    newPerson = NewPerson(["ming","kebi","mao"])
    print(len(newPerson))   #TypeError: object of type 'NewPerson' has no len()
    print(len(newPerson[:]))  #TypeError: 'NewPerson' object is not subscriptable

    对象没有长度和不能切片可以理解。

    示例二:

    class NewPerson(object):
        def __init__(self,p_list):
            self.p_list = p_list
    
        def __getitem__(self, item): 
            return self.p_list[item]
    
    newPerson = NewPerson(["ming","kebi","mao"])
    print(len(newPerson))  #TypeError: object of type 'NewPerson' has no len()
    print(len(newPerson[:]))  #3

    newPerson之所以能够切片是因为__getitem__返回的是一个list,而list肯定可以切片
    newPerson在取值的时候调用了__gititem__这个魔法方法,从而返回list,所以newPerson[]有len

    示例三:

    class NewPerson(object):
        def __init__(self,p_list):
            self.p_list = p_list
    
        def __getitem__(self, item):
            return self.p_list[item]
    
        def __len__(self):
            return len(self.p_list)
    
    newPerson = NewPerson(["ming","kebi","mao"])
    print(len(newPerson))  #3  调用__len__
    print(len(newPerson[:]))  #3  调用__getitem__

    现在我们帮这个对象实现了__len__这个方法,所以这个newPerson对象可以可以直接使用len()
    此时newPerson[:]因为是取值,依旧使用的是__getitem__这个方法。

    3.魔法函数一览

    使用dir()或者__dir__可以或者一个对象已经定义的的魔法函数。
    例如:dir(Person)
    __class__ 等同于type,打印对象类似
    __delattr__
    __dict__ 等同于vars,以键值对的形式返回对象的属性信息
    __dir__
    __doc__ 帮助文档
    __eq__
    __format__
    __ge__
    __getattribute__
    __gt__
    __hash__
    __init__ 构造器
    __init_subclass__
    __le__
    __lt__
    __module__
    __ne__
    __new__
    __reduce__
    __reduce_ex__
    __repr__
    __setattr__
    __sizeof__
    __str__
    __subclasshook__
    __weakref__

    我们将魔法方法分为:非数学运算和数学运算两大类

    1)非数学运算

    (1)字符串
    __repr__
    __str__

    (2)集合序列相关
    __len__
    __getitem__:将当前对象转换为可迭代对象,因而可以切片。
    __setitem__
    __delitem__
    __contains__

    (3)迭代相关
    __iter__
    __next__

    (4)可调用
    __call__

    (5)with上下文管理器
    __enter__
    __exit__

    (6)数值转换
    __abs__
    __bool__
    __int__
    __float__
    __hash__
    __index__

    (7)元类相关
    __new__
    __init__

    (8)属性相关
    __getattr__:没有属性做什么
    __setattr__
    __getattribute__
    __setattribute__
    __dir__

    (9)属性描述符
    __get__
    __set__
    __delete__

    (10)协程
    __await__
    __aiter__
    __anext__
    __aenter__
    __aexit__

    2)数学运算

    (1)一元运算符
    __neg__ (-)
    __pos__ (+)
    __abs__

    (2)二元运算符
    __lt__ (<)
    __le__ (<=)
    __eq__ (==)
    __ne__ (!=)
    __gt__ (>)
    __ge__ (>=)

    (3)算术运算符
    __add__ (+)
    __sub__ (-)
    __mul__ (*)
    __truediv__ (/)
    __floordiv__ (//)
    __mod__ (%)
    __divmod__ 或divmod()
    __pow__ 或pow() (**)
    __round__ 或round()

    (4)反向算术运算符
    __radd__
    __rsub__
    __rmul__
    __rtruediv__
    __rfloordiv__
    __rmod__
    __rdivmod__
    __rpow__

    (5)增量赋值算术运算符
    __iadd__
    __isub__
    __imul__
    __ifloordiv__
    __ipow__

    (6)位运算符
    __invert__ (~)
    __lshift__ (<<)
    __rshift__ (>>)
    __and__ (&)
    __or__ (|)
    __xor__ (^)

    (7)反向位运算符
    __rlshift__
    __rrshift__
    __iand__
    __ixor__
    __ior__

    (8)增量赋值运算符
    __ilshift__
    __irshift__
    __iand__
    __ixor__
    __ior__

    3)__str__、__repr__

    class Person(object):
        def __init__(self,p_list):
            self.p_list = p_list
    person = Person(["ming","UZI","karsa"])
    print(person)  #<__main__.Person object at 0x00000218983ACBE0>
    print会隐式调用str,也就是__str__
    
    现在可以修改一下__str__返回值。
    class Person(object):
        def __init__(self,p_list):
            self.p_list = p_list
        
        def __str__(self):
            return ".".join(self.p_list)  #默认return self
    person = Person(["ming","UZI","karsa"])
    #本来是返回这个对象的内存地址,现在我们修改了返回值。
    print(person)  #ming.UZI.karsa
    
    
    class Person(object):
        def __init__(self,p_list):
            self.p_list = p_list
    
    person = Person(["ming","UZI","karsa"])
    person  #<__main__.Person at 0x218983b47b8>
    隐式调用repr(person)
    
    class Person(object):
        def __init__(self,p_list):
            self.p_list = p_list
        
        def __repr__(self):
            return "+".join(self.p_list)
            
    person = Person(["ming","UZI","karsa"])
    person  #ming+UZI+karsa

    反思:
    我们在写代码的时候,比如定义a = 11,我们如果想获取它的绝对值,就会用abs(a)来进行获取。
    此时我就会想为什么a就能用abs来获取绝对值了?因为它已经定义了

    在init中定义了这个魔法方法,所以你才能够使用这些方法。
    就像上面,如果我们没有定义__len__这个方法,那么按照常理是不能使用len(),
    因为不支持,它是一个对象,对象里面原始定义也没有定义。
    所以我们可以给它实现__len__这个魔法函数,person这个对象就可以使用了。
    比如我们定义了一个类:

    class Num(object): 
    def __init__(self,value):
        self.value = value
    
    a = Num(1)
    print(abs(a))  #TypeError: bad operand type for abs(): 'Num'

    现在a是一个新的类Num的对象,并不是int的对象,所以它是无法使用abs()的。

    我们给它添加魔法函数__abs__之后就可以了。

    class Num(object):
        def __init__(self,value):
            self.value = value
    
        def __abs__(self):
            return abs(self.value)
    a = Num(1)
    print(abs(a))

    比如我们再定义一个:

    class MyVector(object):
        def __init__(self,x,y):
            self.x = x
            self.y = y
        def __add__(self,other_instance):
            return MyVector(self.x + other_instance.x, self.y + other_instance.y)
        def __str__(self):
            return "x:{x}, y:{y}".format(x=self.x, y=self.y)
        
    first_v = MyVector(1,2)
    second_v = MyVector(3,4)
    print(first_v + second_v)  #x:4, y:6

      如果我们不定义__add__,那就是两个对象在加

  • 相关阅读:
    mariadb配置双主多从
    mq系列rabbitmq-02集群+高可用配置
    mq系列rabbitmq-01简介,安装,api操作
    持续集成框架jenkins介绍02-持久集成git仓库+maven项目
    git仓库相关知识03-搭建远程仓库服务器
    RecyclerView瀑布流优化方案探讨
    Android实际开发bug大总结
    Android打造万能自定义阴影控件
    PagerAdapter深度解析和实践优化
    Java博客大汇总
  • 原文地址:https://www.cnblogs.com/yangmingxianshen/p/11281805.html
Copyright © 2011-2022 走看看