类与对象深度问题与解决技巧

如何派生内置不可变类型并修改其实例化行为

定义一种新类型的元组,对于传入的可迭代对象,只保留其中int类型且大于0的元素,例如:

IntTuple([2,-2,'jr',['x','y'],4]) => (2,4)

如何继承内置tuple实现IntTuple

在此之前我们先做一个测试

class Normal(object):
    def __init__(self, item):
        print(self)


class IntTuple(tuple):
    def __init__(self, item):
        print(self)


class MyList(list):
    def __init__(self, item):
        print(self)
        

a = [2,-2,'jr',['x','y'],4]
IntTuple(a)
Normal(a)
MyList(a)
# (2, -2, 'jr', ['x', 'y'], 4)
# <__main__.Normal object at 0x0000022FD88E92E8>
# []

从结果看,继承不同的对象,展现出了不同的效果,为什么呢?我们知道__init__魔法方法是实例化方法,谁调用它它就是谁,为什么在tuple中,self就是传入的对象呢?说明在self之前,该对象已经被创建好了,是谁创建的self呢?

class IntTuple(object):
    # 创建一个新的对象并返回该对象
    def __new__(cls, *args, **kwargs):
        print('__new__', cls, *args)
        return object.__new__(cls)

    def __init__(self, item):
        print('__init__')


a = [2,-2,'jr',['x','y'],4]
IntTuple(a)
# __new__ <class '__main__.IntTuple'> [2, -2, 'jr', ['x', 'y'], 4]
# __init__

# 用另一种方式表达上面的效果
l = list.__new__(list, 'abc')
print(l)
list.__init__(l, 'abc')
print(l)
# 等于 l = list('abc')

t = tuple.__new__(tuple, 'abc')
print(t)
# 等于t = tuple('abc')

# []
# ['a', 'b', 'c']
# ('a', 'b', 'c')

cls 即使IntTuple本身

列表__new__一个对象后是一个空对象,元组__new__一个对象就是对象本身

class IntTuple(tuple):
    def __new__(cls, item):
        # print('__new__', cls, *args)
        f = (i for i in item if isinstance(i, int) and i > 0)
        return super().__new__(cls, f)


a = IntTuple([2, -2, 'jr', ['x', 'y'], 4])
print(a)
# (2, 4)

slots

__slots__的作用是组织实例化类时分配__dict__,默认情况下每个类都有一个dict,通过__dict__访问,这个dict维护了这个实例所有属性,例子如下:

class A(object):
    # 类属性
    x = 1
    def __init__(self):
        pass


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


# {}
# {'id': 1}

可以看到dict不保存类属性,只保存实例属性

__slots__是一个元组,包含了当前能访问到的属性,此时类的实例只能拥有slots中的变量,不能再新增变量。注意,定义了slots后不再有dict

class Base(object):
    __slots__ = ('x', 'y')
    def __init__(self, x):
        self.x = x
        

b = Base(1)
print(b.x)
b.x = 2
print(b.x)
b.y = 3
print(b.y)
b.z = 4
print(b.z)

# 1
# 2
# 3
# AttributeError: 'Base' object has no attribute 'z'

slots中没有z,在实例化中增加z时,出现报错

应用场景

  • 当需要用到的变量是可知的时候
  • 有时候我们只想使用固定的属性,不想任意绑定其他属性

slots在继承中

class Base(object):
    __slots__ = ('x', 'y')
    def __init__(self, x):
        self.x = x


class BaseSON(Base):
    def __init__(self):
        pass    
        

b = Base(1)
bs = BaseSON()
print(dir(b))
print(dir(bs))

# ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'x', 'y']
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

在b中含有__slots__,在BS中含有__dict__,就是说当继承父类(含有__slots__)子类中不受影响,子类可以动态的添加变量。

因为有dict的存在可以动态的增删改,但如果需要大量实例化,会非常耗费内存。

from memory_profiler import profile
class A(object):
    def __init__(self, item):
        self.item = item

@profile
def main():
    # 列表推导式
    f = [A(23333) for i in range(100000)]

if __name__ == '__main__':
    main()
    
    
Line #    Mem usage    Increment   Line Contents
================================================
     6     18.1 MiB     18.1 MiB   @profile
     7                             def main():
     8     38.1 MiB      0.7 MiB       f = [A(23333) for i in range(100000)]

Mem usage表示该行运行后,python解释器的内存使用情况

Increment 表改行代码执行前后的内存变化

MiB,mebibyte(1Mib = 1.05MB)

memory_profiler也很消耗内存,@profile用来标记需要追踪的代码

from memory_profiler import profile
class A(object):
    # 使用slots
    __slots__ = 'item'
    def __init__(self, item):
        self.item = item

@profile
def main():
    f = [A(23333) for i in range(100000)]

if __name__ == '__main__':
    main()
    
# Line #    Mem usage    Increment   Line Contents
================================================
     7     18.1 MiB     18.1 MiB   @profile
     8                             def main():
     9     23.7 MiB      0.3 MiB       f = [A(23333) for i in range(100000)]

使用slots前内存消耗0.7MiB,使用后消耗0.3

参考自:https://blog.csdn.net/sxingming/article/details/52892640

上下文管理器

我们知道通过with打开某个CSV,操作过后不需要手动close,它可以自动关闭。如果用with调用一个对象呢?

class file_text(object):
    def file(self):
        print('我裂开了')


with file_text as ft:
    ft.file()

#AttributeError: __enter__

属性错误

class file_text(object):
    # 打开资源
    def __enter__(self):
        print('start')
        # 需要返回self对象,否则报错
        return self

    def demo(self):
        print('我裂开了')

    # 释放资源
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('end')


with file_text() as ft:
    ft.demo()

exc_type,异常类

exc_val,异常值

exc_tb,追踪信息

import contextlib


@contextlib.contextmanager
def file_open(filename):
    # 相当于__enter__方法
    print('open')
    yield {}
    # 相当于__exit__方法
    print('end')


with file_open('c/VCC') as fo:
    print('file operation')

# open
# file operation
# end

如何创建可管理的对象

class A(object):
    def __init__(self, age):
        self.age = age

    def get(self):
        return self.age

    def set(self, age):
        if not isinstance(self.age, int):
            raise TypeError('需要传入整数')
        else:
            self.age = age

    # x = property(getx, setx, delx, "I'm the 'x' property.")
    x = property(get, set)


a = A(18)
a.x = 20
print(a.x)

# 20

类之间的比较

class rect(object):
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h

    def __str__(self):
        return '求矩形的面积'

    def __lt__(self, other):
        return self.area() < other.area()


r = rect(1, 2)
r2 = rect(3, 4)
print(r < r2)
print(r)
  • __str__,打印实例对象时,返回字符串
  • __eq__(self, other), ==
  • __ne__(self, other),!=
  • __lt__,<
  • __gt__,>
  • __le__,<=
  • __ge__,>=
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值