文章目录
类与对象深度问题与解决技巧
如何派生内置不可变类型并修改其实例化行为
- 我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素,例如:
- IntTuple([2,-2,‘jr’,[‘x’,‘y’],4]) => (2,4)
- 思路:
- 对于tuple类来说,在__new__方法中就已经把传入的序列变成了元组,而且元组是不可以修改的,所以不可以在__init__中操作,需要在__new__中操作
- 代码:
-
# class IntTuple(tuple): # def __init__(self,iterable): # # for i in iterable: # # if isinstance(i,int) and i >0: # # super().__init__() # print(self) # # int_t = IntTuple([2,-2,'jr',['x','y'],4]) # # print(int_t) #2,4 # class B: # pass # # class A(B): # def __new__(cls, *args, **kwargs): # print('A.__new__',cls,args) # # return object.__new__(cls) # return super().__new__(cls) # # def __init__(self,*args): # print('A.__init__') # # a = A(1,2) # a = A.__new__(A,1,2) # A.__init__(a,1,2) class IntTuple(tuple): def __new__(cls, iterable): # 生成器 f = (i for i in iterable if isinstance(i,int) and i >0) return super().__new__(cls,f) int_t = IntTuple([2,-2,'jr',['x','y'],4]) print(int_t)
-
如何为创建大量实例节省内存
- 定义类的__slots__属性,声明实例有哪些属性关闭动态绑定
- 查看对应的代码占用内存情况
- sys模块:sys.getsizeof(对应代码)
- 跟踪内存的使用情况,更具体看消耗了多少内存
- 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定义了允许绑定的属性名 __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('00001','a') p2 = Player2('00002','b') # print(dir(p1)) # print(len(dir(p1))) # print(dir(p2)) # print(len(dir(p2))) # __weakref__ 弱引用 # __dict__ 动态绑定属性 # print(set(dir(p1))-set(dir(p2))) #{'__weakref__', '__dict__'} # p1.x = 6 # p1.__dict__['y'] = 7 # print(p1.__dict__) # 多了实例化属性 x 和y # p2.x = 8 #会报错因为__slots__规定的属性名中没有x # import sys # 可以用sys中的函数来看 动态添加属性的__dict__占用了多少内存 # # print(sys.getsizeof(p1.__dict__)) # print(sys.getsizeof(p1.name)) # print(sys.getsizeof(p2.uid)) import tracemalloc #跟踪内存的使用 tracemalloc.start() p1 = [Player1(1,2,3) for _ in range(100000)] #size=6274 KiB # p2 = [Player2(1,2,3) for _ in range(100000)] #size=7837 KiB end = tracemalloc.take_snapshot() # top = end.statistics('lineno') #单行代码显示 top = end.statistics('filename') #全部代码行内存使用一起显示 for start in top[:10]: print(start)
-
上下文管理器
- 文件读取时,是需要打开、读取、关闭三个步骤,with语句可以简化操作,contextlib模块也可以简化操作
- with语句
-
with open('filename'.'operate_type') as f: f.read()
-
- contextlib:看代码文件
- 代码
-
# try: # f = open('test.txt','w') # print('code') # raise KeyError # except KeyError as e : # print('Key Error') # f.close() # except IndexError as e: # print('Index Error') # except Exception as e : # print(e) # f.close() # finally: # # 不管有没有异常 都会运行 # print('end') # f.close() """ 上下文管理器可以简化上面文件打开和关闭操作 """ # with open('test.txt','w') as f: # f.read() # 用类来支持上下文管理器 class Sample(object): # 获取资源 def __enter__(self): print('start') return self def demo(self): print('this is demo') # 释放资源 # 这里的exc_type, exc_val, exc_tb参数正是简化了异常处理 def __exit__(self, exc_type, exc_val, exc_tb): # 异常类型 print(exc_type) # 异常值 print(exc_val) # 异常追踪信息 print(exc_tb) print('end') # with Sample() as sample: # sample.demo() import contextlib #简化上下文管理器 @contextlib.contextmanager def file_open(filename): print('file open') yield {} #yield上面代码相当于__enter__函数,下面代码相当于__exit_函数 print('file close') with file_open('demo.txt') as f : print('fle operation')
-
如何创建可管理的对象属性
- 创建访问器和设置器
- property()函数
- 装饰器property
- 代码
-
""" 麻烦 A.get_key() #访问器 A.set_key() #设置器 需要达到下面条件 形式上:属性访问 实际上:调用方法 """ """ class A: def __init__(self,age): self.age = age a = A(12) # a.age=20 a.age = '20' #如果这时这个age是从文件读取出来的 传入的20为字符串的话,其实是不符合的,需要实现判断类型的功能,通过访问器和设置器 print(type(a.age)) """ 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 # 通过property()函数实现 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(10) # a.set_age('20') # print(a.get_age()) # 使用property()函数 # a.R=20 # print(a.R) # 使用装饰器property a.S=20 print(a.S)
-
如何让类支持比较操作
- 重写类内的__lt__()和__eq__()等方法
- 使用抽象基类来简化两个比较的类的代码
- 代码
-
""" 整数字符串类型比较时是调用__lt__()、__gt__()...等方法 字符串也可以调用ord()方法去比较,字符串是比较AscII码 集合进行比较的时候前面集合是否包含后面集合例如:{1,2,3}>{1,2}->True """ """ from functools import total_ordering import math @total_ordering class Rect(object): def __init__(self,l,w): self.l = l self.w = w def area(self): return self.l*self.w def __str__(self): return '(%s,%s)'%(self.l,self.w) def __lt__(self, other): return self.area() < other.area() def __eq__(self, other): return self.area()==other.area() #要重写内置比较方法的话,还要写大于等于方法,会比较麻烦,这时候需要装饰器 #加上了total_ordering装饰器后只需要写小于和等于方法,其他大于、大于等于、小于方法等于会自动完成 rect1 = Rect(1,2) rect2 = Rect(3,4) # print(rect1.area() < rect2.area()) #可以通过调用里面方法进行比较,但不是类之间进行比较 print(rect1 < rect2) #类之间进行比较 True print(rect1 > rect2) #类中没写__gt__,但还是可以执行,因为python类中写了lt方法后,会自动形成大于方法 print(rect1 == rect2) print(rect1 >= rect2) class Circle(object): 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 = Circle(2) rect = Rect(1,2) print(c==rect) """ #使用类的继承和抽象的基类 来优化两个类 from functools import total_ordering import math from abc import ABCMeta,abstractmethod #使用抽象基类来简化代码 class Shape(metaclass=ABCMeta): @abstractmethod #代表area方法子类中必须重写 def area(self): pass #公共方法 def __lt__(self, other): return self.area() < other.area() # 公共方法 def __eq__(self, other): return self.area() == other.area() @total_ordering class Rect(Shape): def __init__(self,l,w): self.l = l self.w = w def area(self): return self.l*self.w # def __str__(self): # return '(%s,%s)'%(self.l,self.w) #要重写内置比较方法的话,还要写大于等于方法,会比较麻烦,这时候需要装饰器 #加上了total_ordering装饰器后只需要写小于和等于方法,其他大于、大于等于、小于方法等于会自动完成 rect1 = Rect(1,2) rect2 = Rect(3,4) # print(rect1.area() < rect2.area()) #可以通过调用里面方法进行比较,但不是类之间进行比较 print(rect1 < rect2) #类之间进行比较 True print(rect1 > rect2) #类中没写__gt__,但还是可以执行,因为python类中写了lt方法后,会自动形成大于方法 print(rect1 == rect2) print(rect1 >= rect2) class Circle(Shape): def __init__(self,r): self.r = r def area(self): return self.r**2*math.pi c = Circle(2) rect = Rect(1,2) print(c==rect)
-
如何在环状数据结构中管理内存(只做了解)
- 例:双向循环链表
- 弱引用网站:https://www.jianshu.com/p/0cecea85ae3b
- 代码
-
import weakref #使用弱引用,弱引用不占用 引用计数 # 每个对象都有一个引用计数,当这个引用计数为0时Python能够安全地销毁这个对象。 # 双向链表 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 node.left = weakref.ref(self) #如果在环状数据结构中(这次例子是双向链表)不使用弱引用创建链表之间的虚拟连接,则创建的对象不会被销毁。 def __str__(self): return 'Node:<%s>' % self.data def __del__(self): #该方法在实例化创建的对象不使用时(即下面的变量head指向了None),对象类会自动调用该方法来销毁对象 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...')
-
实例方法名字的字符串调用方法
- 问题:分别有矩形类、圆形类、三角形类,里面求面积的方法名字都不同,怎么样通过字符串方法来调用每个类中的方法
- getattr(object, name[, default])
- getattr() 函数用于返回一个对象属性值
- object – 对象。
name – 字符串,对象属性。
default – 默认返回值,如果不提供该参数,在没有对应属性时,将触发 AttributeError。
- map(fun, iterable)
- map()函数的主要作用是可以把一个方法依次执行在一个可迭代的序列上
- 基础语法:map(fun, iterable)
参数:fun是map传递给定可迭代序列的每个元素的函数。iterable是一个可以迭代的序列,序列中的每一个元素都可以执行fun
返回值:map object
- 代码:
-
import math class Circle(): def __init__(self,r): self.r =r def area(self): return self.r**2*math.pi class Triangle(): def __init__(self,a,b,c): self.a = a self.b = b self.c = 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 = a self.b = b def getArea(self): return self.a*self.b shape_c = Circle(2) shape_t = Triangle(2,2,3) shape_r = Rectangle(4,5) def get_area(shape): names = ['area','get_area','getArea'] for name in names: f = getattr(shape,name,None) if f: return f() print(get_area(shape_c)) print(get_area(shape_t)) print(get_area(shape_r)) shape = [shape_c,shape_r,shape_t] list = list(map(get_area,shape)) print(list)
-