__slots__和运算符重载中的反向方法

问题的引出

都是字典惹的祸

字典为了提升查询效率,必须用空间换时间。

一般来说一个多想,属性多一点,都存储在字典中便于查询,问题不大。

但是如果数百万个对象,那么字典占的就有点大了。

这个时候,能不能把属性字典__dict__省了?

python提供了__slots__

class A:
    x = 1

    def __init__(self):
        self.y = 5
        self.z = 6

    def show(self):
        print(self.x,self.y,self.z)

a = A()
print(A.__dict__)
print(a.__dict__)

结果为:
{‘__module__‘: ‘__main__‘, ‘x‘: 1, ‘__init__‘: <function A.__init__ at 0x0000000001E670D8>, ‘show‘: <function A.show at 0x0000000001E674C8>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘A‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘A‘ objects>, ‘__doc__‘: None}
{‘y‘: 5, ‘z‘: 6}

思考

上面2个字典,谁的字典是个问题?

实例多达百万个的时候,这么多存放实例属性的字典是个问题。

class A:
    x = 1
    __slots__ = ("y")#元组
    #__slots__ = ["y","z"]#可以吗?
    # __slots__ = "y","z"#可以吗?
    # __slots__ = "y"

    def __init__(self):
        self.y = 5
        self.z = 6

    def show(self):
        print(self.x,self.y)

a = A()
a.show()
print("A",A.__dict__)
#print("OBJ",a.__dict__)
print(a.__slots__)

结果为:
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 17, in <module>
    a = A()
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 11, in __init__
    self.z = 6
AttributeError: ‘A‘ object has no attribute ‘z‘

上面报错,因为slots,只有一个槽位。直接就报错了。

class A:
    x = 1
    __slots__ = ("y")#元组

    def __init__(self):
        self.y = 5
        #self.z = 6

    def show(self):
        print(self.x,self.y)

a = A()
a.show()
print("A",A.__dict__)
print(a.__dict__)

结果为:
1 5
A {‘__module__‘: ‘__main__‘, ‘x‘: 1, ‘__slots__‘: ‘y‘, ‘__init__‘: <function A.__init__ at 0x0000000001E560D8>, ‘show‘: <function A.show at 0x0000000001E564C8>, ‘y‘: <member ‘y‘ of ‘A‘ objects>, ‘__doc__‘: None}
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 18, in <module>
    print(a.__dict__)
AttributeError: ‘A‘ object has no attribute ‘__dict__‘

上面又报错了。

class A:
    x = 1
    __slots__ = ("y","z")#元组
    #__slots__ = ["y","z"]#可以吗?
    # __slots__ = "y","z"#可以吗?
    # __slots__ = "y"

    def __init__(self):
        self.y = 5
        #self.z = 6

    def show(self):
        print(self.x,self.y)

a = A()
a.show()
print("A",A.__dict__)
#print("OBJ",a.__dict__)
print(a.__slots__)

结果为:
1 5
A {‘__module__‘: ‘__main__‘, ‘x‘: 1, ‘__slots__‘: (‘y‘, ‘z‘), ‘__init__‘: <function A.__init__ at 0x00000000026770D8>, ‘show‘: <function A.show at 0x00000000026774C8>, ‘y‘: <member ‘y‘ of ‘A‘ objects>, ‘z‘: <member ‘z‘ of ‘A‘ objects>, ‘__doc__‘: None}
(‘y‘, ‘z‘)

__slots__告诉解释器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是使用元组比较好。

一旦类提供了__slots__,就阻止实例产生__dict__来保存实例的属性。

尝试为实例a动态增加属性

a.newx = 5

说明实例不可以动态增加属性了

A.NEWX = 20,这是可以的,因为这是类属性。

继承

class A:
    x = 1
    __slots__ = ("y","z")#元组

    def __init__(self):
        self.y = 5
        #self.z = 6

    def show(self):
        print(self.x,self.y)

a = A()
a.show()
print("A",A.__dict__)
print(a.__slots__)

class B(A):
    def __init__(self):
        self.b1 = 500

print("B",B().__dict__)

结果为:

1 5
A {‘__module__‘: ‘__main__‘, ‘x‘: 1, ‘__slots__‘: (‘y‘, ‘z‘), ‘__init__‘: <function A.__init__ at 0x0000000001E87E58>, ‘show‘: <function A.show at 0x0000000001E87438>, ‘y‘: <member ‘y‘ of ‘A‘ objects>, ‘z‘: <member ‘z‘ of ‘A‘ objects>, ‘__doc__‘: None}
(‘y‘, ‘z‘)
B {‘b1‘: 500}

__slots__不影响子类实例,不会继承下去,查费子类里面自己也定义了__slots__。

应用场景

使用需要构建数百万以上对象,且内存容量较为紧张,实例的属性简单,固定且不用动态增加的场景。

未实现和未实现异常

print(type(NotImplemented))
print(type(NotImplementedError))

#<class ‘NotImplementedType‘>
#<class ‘type‘>

raise NotImplemented
#raise NotImplementedError

结果为;
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 7, in <module>
    raise NotImplemented
TypeError: exceptions must derive from BaseException
<class ‘NotImplementedType‘>
<class ‘type‘>
NotImplemented是个值,单值,是NotImplementedType类的实例。
NotImplementedError是类型,是异常,返回type。

运算符重载中的反向方法

前面学习过运算符重载的方法,例如add和iadd.

class A:
    def __init__(self,x):
        self.x = x

    def __add__(self, other):#+
        print(self,"add")
        return self.x+other.x

    def __iadd__(self, other):#+=
        print(self,"iadd")
        return A(self.x + other.x)

    def __radd__(self, other):#+
        print(self,"radd")
        return self.x + other.x

a = A(4)
b = A(5)
print(a,b)
print(a+b)
print(b+a)
b+=a
a+=b

结果为:
<__main__.A object at 0x00000000021FA248> <__main__.A object at 0x00000000021FA348>
<__main__.A object at 0x00000000021FA248> add
9
<__main__.A object at 0x00000000021FA348> add
9
<__main__.A object at 0x00000000021FA348> iadd
<__main__.A object at 0x00000000021FA248> iadd

__radd__方法根本没有执行过?为什么?

因为都是A的实例,都是调用的__add__,无非就是实例a还是实例b调用而已。

测试一下a+1

class A:
    def __init__(self,x):
        self.x = x

    def __add__(self, other):
        print(self,"add")
        return self.x+other.x

    def __iadd__(self, other):
        print(self,"iadd")
        return A(self.x + other.x)

    def __radd__(self, other):
        print(self,"radd")
        return self.x + other.x

a = A(4)
a+1

结果为:
<__main__.A object at 0x000000000222B248> add
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 19, in <module>
    a+1
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 7, in __add__
    return self.x+other.x
AttributeError: ‘int‘ object has no attribute ‘x‘

出现了AttributeError,因为1是int类型,没有x这个属性,还是__add__被执行了。

测试1+a,运行结果如下:

<__main__.A object at 0x00000000021EB208> radd
Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 19, in <module>
    1+a
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 15, in __radd__
    return self.x + other.x
AttributeError: ‘int‘ object has no attribute ‘x‘

这次执行的是实例a的__radd__方法。

1+a等价于1.__add__(a),而类型实现了__add__方法的,为什么却不抛出异常,而是执行了实例a的__radd__方法?

再看一个例子

class A:
    def __init__(self,x):
        self.x = x

    def __add__(self, other):
        print(self,"add")
        return self.x+other.x

    def __iadd__(self, other):
        print(self,"iadd")
        return A(self.x + other.x)

    def __radd__(self, other):
        print(self,"radd")
        return self.x + other.x     #return self+other #相当于self.__add__(other)

class B:#未实现__add__
    def __init__(self,x):
        self.x = x

a = A(4)
b = B(10)
print(a+b)
print(b+a)

结果为:
<__main__.A object at 0x00000000021EB408> add
14
<__main__.A object at 0x00000000021EB408> radd
14

b+a等价于b.__add__(a),但是类B没有实现__add__方法,就去找a的__radd__方法。

1+a等价于1.__add__(a),而int类型实现了__add__方法的,不过这个方法对于这种加法的返回值是notlmplemented,解释器发现是这个值,就会发起对第二操作对象的__radd__方法的调用。

1+a能解决吗?

class A:
    def __init__(self,x):
        self.x = x

    def __add__(self, other):
        print(self,"add")
        try:
            x = other.x
            return self.x + other.x
        except AttributeError:
            try:
                x = int(other)
            except:
                x = 0
            return self.x +x

    def __iadd__(self, other):
        print(self,"iadd")
        return A(self.x + other.x)

    def __radd__(self, other):
        print(self,"radd")
        return self.x + other.x

class B:
    def __init__(self,x):
        self.x = x

a = A(4)
b = B(10)
print(a+b)
print(b+a)
print(a+2)
print(2+a)
print(a+"abc")
print("abc"+a)

结果为:
Traceback (most recent call last):
<__main__.A object at 0x00000000021DA408> add
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 36, in <module>
    print(2+a)
14
  File "C:/Users/Administrator/PycharmProjects/studytset/xpc1/test1.py", line 24, in __radd__
<__main__.A object at 0x00000000021DA408> radd
    return self.x + other.x
14
AttributeError: ‘int‘ object has no attribute ‘x‘
<__main__.A object at 0x00000000021DA408> add
6
<__main__.A object at 0x00000000021DA408> radd

“abc”+a,字符串也实现了__add__方法,不过默认是处理不了和其他类型的加法,就返回notlmplemented。

class A:
    def __init__(self,x):
        self.x = x

    def __add__(self, other):
        print(self,"add")
        try:
            res = self.x + other.x
        except:
            try:
                o = int(other)
            except:
                o = 0
            res = self.x+o
        return res

    def __iadd__(self, other):
        print(self,"iadd")
        return A(self.x + other.x)

    def __radd__(self, other):
        print(self,"radd")
        return self.x + other.x

class B:
    def __init__(self,x):
        self.x = x

a = A(4)
b = B(10)
print(a+b)
print(b+a)
print(a+2)
print(2+a)
 

原文地址:https://www.cnblogs.com/xpc51/p/12012506.html

时间: 2024-10-02 01:49:43

__slots__和运算符重载中的反向方法的相关文章

(一)Python入门-6面向对象编程:10特殊方法和运算符重载-特殊属性

一:特殊方法和运算符重载 Python的运算符实际上是通过调用对象的特殊方法实现的.比如: #运算符-特殊方法 a = 20 b = 30 c = a + b d = a.__add__(b) print('c=',c) print('d=',d) 运行结果: c= 50 d= 50 常见的特殊方法统计如下: 每个运算符实际上都对应了相应的方法,统计如下: 我们可以重写上面的特殊方法,即实现了“运算符的重载”. [操作]运算符的重载 #测试运算符的重载 class Person: def __i

队列(queue) 之 c++模板实现(友元函数和运算符重载)

一:起因 (0)拿出自己年初实现的queue队列,第一次用c++类实现queue,在和如今实现的其他复杂的STL对比,心情无比复杂: 注释:看到听到当年自己的所写所想,正的是一种享受 -- 倾听自己的幼稚也是一种美. (1)闲话少说了,我自己现在回答自己的 三 (5) 中提到的问题,函数的返回值是用bool型还是void型??其实函数返回值是bool 还是 void是视情况而定的:例如,判空函数bool isEmpty(),比较运算符重载函数 bool operator >=(&)等判断函数

go语言笔记——是c开发的 lex yacc进行词法和语法分析,go不支持函数和运算符重载,不支持类型继承,也不支持断言,还有泛型

从 Go 1.0.3 版本开始,不再使用 8g,8l 之类的指令进行程序的构建,取而代之的是统一的 go build 和 go install 等命令,而这些指令会自动调用相关的编译器或链接器. 如果你想获得更深层次的信息,你可以在目录 $GOROOT/src/cmd 下找到编译器和链接器的源代码.Go 语言本身是由 C 语言开发的,而不是 Go 语言(Go 1.5 开始自举).词法分析程序是 GNU bison,语法分析程序是名为 $GOROOT/src/cmd/gc/go.y 的 yacc

pyhton中的魔术方法

魔术方法 ***** 特殊属性 属性 说明 __name__ 类.函数.方法等的名字 __module__ 类定义所在的模块名 __class__ 对象或类所属的类 __bases__ 类的基类的元组,顺序为它们在基类列表中出现的顺序 __doc__ 类.函数的文档字符串,如果没有定义则为 None __mro__ 类的mro,class.mro()返回的结果的保存在 __mro__ 中 __dict__ 类或实例的属性,可写的字典 查看属性 __dir__ 方法 方法 说明 __dir__ 返

第二章:数据类型和运算符

第二章:数据类型和运算符 计算机中的进制 **标识符 总的命名规则:见名知意.如果有多个单词组成,首单词小写,其余单词的首字母大写(驼峰命名法).1.首字母只能是字母,下划线和$2.其余字母可以字母,下划线,$和数字3.不能使用预留关键字4.严格区分大小写 数据类型*** 基本数据类型 整型 byte(1个字节) short(2个字节) int(4个字节) long(8个字节) 浮点型 float(4个字节) double(8个字节) 字符型 char(2个字节)采用Unicode码 布尔型 b

java中的数据类型和运算符的总结归类。

首先学习java肯定先要了解java的发展史,以及java的特点,常见的dos命令,jdk的安装,如何开发java程序等等一下概念行的东西,这里面我都不一一说了. 今天这一章主要想总结一下java中的数据类型和运算符2大方面. 再说数据类型之前先说一下标识符的命名规则: 总的命名规则:见名知意.如果有多个单词组成,首单词小写,其余单词的首字母大写(驼峰命名法).1.首字母只能是字母,下划线和$2.其余字母可以字母,下划线,$和数字3.不能使用预留关键字4.严格区分大小写(总体来说和c语音一样)

JAVA中继承时方法的重载(overload)与重写/覆写(override)

JAVA继承时方法的重载(overload)与重写/覆写(override) 重载-Override 函数的方法参数个数或类型不一致,称为方法的重载. 从含义上说,只要求参数的个数或参数的类型不一致就说两个函数是重载函数,而至于返回值是否一样,没关系.同时,重载可以发生在同一个类中也可以发生在继承关系中. class A { } class B extends A { public void fun(String data1) { System.out.println(data1); } pub

java中的方法重载与重写以及方法修饰符

1. 方法重载Overloading , 是在一个类中,有多个方法,这些方法的名字相同,但是具有不同的参数列表,和返回值 重载的时候,方法名要一样,但是参数类型和参数个数不一样,返回值类型可以相同,也可以不同, 不能以返回值类型判断方法是否重载. 2. 方法重写Overriding , 是存在于父类与子类之间 (1)若子类中的方法与父类中的某一方法具有相同的方法名.返回类型和参数表,则新方法覆盖父类中的方法,如需调 用父类方法用super关键字 (2)子类的重写方法的权限修饰符不能小于父类的,要

python中函数和方法区别,以及如何给python类动态绑定方法和属性(涉及types.MethodType()和__slots__)

网上有很多同义但不同方式的说法,下面的这个说法比较让你容易理解和接受 1 与类和实例无绑定关系的function都属于函数(function): 2 与类和实例有绑定关系的function都属于方法(method). “与类和实例无绑定关系”就道出了其中的关键 我们知道python是动态的编程语言,python的类除了可以预先定义好外,还可以在执行过程中,动态地将函数绑定到类上,绑定成功后,那些函数就变成类的方法了. 定义User类 可以使用__slots__来限制绑定的属性和方法 1 user