Python高级编程技巧第2讲 - 对象深度问题与解决技巧

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

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

IntTuple([2,-2,'jr',['x','y'],4])    =>     (2,4)
class IntTuple(tuple):
    def __new__(cls, iterable):
        # for i in iterable:
        #     if isinstance(i,int) and i > 0:
        #         super().__init__(i)
        # print(self)

        # 生成器
        f = (i for i in iterable if isinstance(i, int) and i > 0)
        # f1 = [i for i in iterable if isinstance(i, int) and i > 0]
        return super().__new__(cls, f)
        # return super().__new__(cls, f1)


        # cls换成IntTuple   这个属于硬编码,不推荐,要改的时候很麻烦
        # return super().__new__(IntTuple, f1)

int_t = IntTuple([2,-2,'jr',['x','y'],4])
print(int_t)
结果: (2, 4)

二、如何为创建大量实例节省内存

  • 解决办法:定义类的__slots__属性,声明实例有哪些属性(关闭动态绑定)
import sys
import tracemalloc

class Player1(object):
    def __init__(self, uid, name, status=0, level=1):
        self.uid = uid
        self.name = name
        self.status = status
        self.level = level

class Player2(object):
    __slots__ = ('uid', 'name', 'status', 'level')
    def __init__(self, uid, name, status=0, level=1):
        self.uid = uid
        self.name = name
        self.status = status
        self.level = level

p1 = Player1('0001','yxy')
p2 = Player2('0002','fgf')

# print(dir(p1))
# print(dir(p2))

# __weakref__弱引用
# __dict__ 动态绑定属性 很浪费内存
# print(set(dir(p1)) - set(dir(p2)))   # {'__weakref__', '__dict__'}

# getsizeof可以查看变量类型、对象占用的内存多少
# print(sys.getsizeof(p1.__dict__))   # 68
# print(sys.getsizeof(p1.name))       # 28
# print(sys.getsizeof(p1.uid))        # 29


# 跟踪内存的使用
tracemalloc.start()
#  _ 循环并不需要的,可以说是一个占位
#  p1没有关动态属性    p2关闭了动态属性,节省了内存
p1 = [Player1(1,2,3) for _ in range(100000)]  # size=14.1 MiB
p2 = [Player2(1,2,3) for _ in range(100000)]  # size=36 B
end = tracemalloc.take_snapshot()
# top = end.statistics('lineno')   # 一行的
top = end.statistics('filename')   # 整个文件的

for start in top[:10]:
    print(start)
  • __weakref__弱引用
  • 动态绑定属性 很浪费内存__dict__
  • getsizeof可以查看变量类型、对象占用的内存多少

三、Python中的with语句

实现:

class Sample(object):

    # **获取资源
    def __enter__(self):
        print('start')
        return self

    def demo(self):
        print('this is demo')

    # **释放资源 Traceback
    def __exit__(self, exc_type, exc_val, exc_tb):
        # 异常类-exc_type    <class 'AttributeError'>
        # 异常值-exc_val     'Sample' object has no attribute 'demos'
        # 追踪信息-exc_tb      <traceback object at 0x00A75A08>
        print(exc_type,'-')
        print(exc_val,'-')
        print(exc_tb,'-')
        print('exit')

with Sample() as sample:
    sample.demos()
  • 异常类 - exc_type: <class ‘AttributeError’>
  • 异常值 - exc_val: ‘Sample’ object has no attribute ‘demos’
  • 追踪信息 - exc_tb: <traceback object at 0x00A75A08>

3.1 上下文管理器协议

contextlib简化上下文管理器

import contextlib

@contextlib.contextmanager
def file_open(filename):
    # XXX
    print('file open')   # 相当于__enter__ 函数
    yield {}
    print('file close')    # 相当于__exit __ 函数


with file_open('demo.txt') as f:
    print('file operation')

四、 如何创建可管理的对象属性

'''
麻烦
A.get_key()     # 访问器
A.set_key()     # 设置器

A.key
A.key = 'jr'
形式上  属性访问
实际上  调用方法
'''

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

    def get_age(self):
        return self.age

    def set_age(self, age):
        if not isinstance(age, int):
            raise TypeError('Type Error')
        self.age = age

    # property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
    R = property(get_age, set_age)

    @property       # 默认是 get
    def S(self):
        return self.age

    @S.setter       # set
    def S(self, age):
        if not isinstance(age, int):
            raise TypeError('Type Error')
        self.age = age


a = A(18)

# 文件读取的  str
# a.age = '20'
# print(type(a.age))
# a.set_age('20')
# print(a.get_age())
# a.R = 20
# print(a.R)

print(a.S)
a.S = 22

五、如何让类支持比较操作

有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较,我们自定义比较的行业,例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积

from functools import total_ordering
import math
import abc


@total_ordering
class Shape(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def area(self):
        pass

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

    def __eq__(self, other):
        return self.area() == other.area()


class Rect(Shape):
    def __init__(self, w, h):
        self.w = w
        self.h = h

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

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

# rect1 = Rect(1, 2)
# rect2 = Rect(1, 2)
# print(Rect(1, 2) > Rect(1, 2))    # rect2 < rect1

class Cirle(Shape):
    def __init__(self, r):
        self.r = r

    def area(self):
        return self.r ** 2*math.pi

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


c = Cirle(8)
rect = Rect(1, 2)

print(c == rect)    # False

六、如何在环状数据结构中管理内存

双向循环链表

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

    def add_right(self, node):
        self.right = node
        node.left = self

    def __str__(self):
        return 'Node:<%s>' % self.data

    def __del__(self):
        print('in __del__: delete %s' % self)

def create_linklist(n):
    head = current = Node(1)
    for i in range(2, n + 1):
        node = Node(i)
        current.add_right(node)
        current = node
    return head

head = create_linklist(1000)
head = None

import time
for _ in range(1000):
    time.sleep(1)
    print('run...')
input('wait...')

我们有三个图形类
Circle,Triangle,Rectangle

class Triangle:
    def __init__(self,a,b,c):
        self.a,self.b,self.c = a,b,c
    
    def get_area(self):
        a,b,c = self.a,self.b,self.c
        p = (a+b+c)/2
        return (p * (p-a)*(p-b)*(p-c)) ** 0.5

class Rectangle:
    def __init__(self,a,b):
        self.a,self.b = a,b
    
    def getArea(self):
        return self.a * self.b
        
class Circle:
    def __init__(self,r):
        self.r = r
    
    def area(self):
        return self.r ** 2 * 3.14159
发布了46 篇原创文章 · 获赞 4 · 访问量 1291

猜你喜欢

转载自blog.csdn.net/Yauger/article/details/103758912
今日推荐