Python编程技巧笔记二:类与对象深度问题与解决技巧

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

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

  • 我们想自定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中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)
      
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值