python高级编程学习——02—(类与对象深度问题与解决技巧、with语句、属性访问、类的比较操作、双向循环链表的弱引用)

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

self对象是谁创建的?

# self对象是谁创建的?
# __new__方法

class B:
    pass

class A(B):
    def __new__(cls, *args, **kwargs):
        print("A.__new__", cls, args)
        # return object.__new__(cls)         # 这里如果注释掉,下面的init方法不会执行         需要返回object
        return super().__new__(cls)          # 这里语句可以代替上面的执行                建议写上面的object.__new__(cls)
    
    def __init__(self, *args):
        print("A.__init__")
        

a = A(1, 2)
# A.__new__ <class '__main__.A'> (1, 2)
# A.__init__

a = A.__new__(A, 1, 2)                            # 上面输出的语句可以翻译为下面执行的两句代码
# A.__new__ <class '__main__.A'> (1, 2)

A.__init__(a, 1, 2)
# A.__init__
class IntTuple(tuple):
    def __new__(cls, iterable):
        
        # f = (i for i in iterable if isinstance(i, int) and i > 0)   # 生成器
        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)     # 想要输出2,4

创建list的分解过程

C:\Users\Administrator
λ ipython
Python 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:25:24) [MSC v.1900 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.8.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: list('abc')
Out[1]: ['a', 'b', 'c']

In [2]: l = list.__new__(list,'abc')

In [3]: l
Out[3]: []

In [4]: list.__init__(l,'abc')

In [5]: l
Out[5]: ['a', 'b', 'c']

# 上面两语句代表的是创建list的分解过程

In [6]: tuple('abc')
Out[6]: ('a', 'b', 'c')

In [7]: t = tuple.__new__(tuple,'abc')

In [8]: t
Out[8]: ('a', 'b', 'c')

In [9]: tuple.__init__(t,'abc')

In [10]: t
Out[10]: ('a', 'b', 'c')

# 元祖不可修改的原因:在new方法中就已经创建好了,不需要init方法,所以通过init方法不能修改元祖数据

2.如何为创建大量实例节省内存
在游戏中,定义了玩家类player,每有一个在线玩家,在服务器内则有一个player的实例,当在线人数很多时,将产生大量实例(百万级)

如何降低这些大量实例的内存开销?

解决方案:
定义类的__slots__属性,声明实例有哪些属性(关闭动态绑定)

import sys    # 导入系统模块

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('001', 'a')
p2 = Player2('002', 'b')

print(dir(p1))
print(len(dir(p1)))    # 30

print(dir(p2))
print(len(dir(p2)))    # 29

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

p1.x = 6
print(p1.__dict__)                       # {'uid': '001', 'name': 'a', 'status': 0, 'level': 1, 'x': 6}
# __dict__   动态绑定属性   极其浪费内存
p1.__dict__['y'] = 7
print(p1.__dict__)                       # {'uid': '001', 'name': 'a', 'status': 0, 'level': 1, 'x': 6, 'y': 7}


# p2.y = 7   # AttributeError: 'Player2' object has no attribute 'y'
print(p2.name)


# p2.__slots__ = ('x')   # AttributeError: 'Player2' object attribute '__slots__' is read-only
# 防止动态绑定

print(sys.getsizeof(p1.__dict__))            # 占用内存152   50+52<152  多出来的内存就是__dict__占用的   浪费内存
print(sys.getsizeof(p1.name))                # 占用内存50
print(sys.getsizeof(p1.uid))                 # 占用内存52
这部分代码只需要了解,需要使用就复制

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('filename')    # 监控整个文件占用内存     size=28.3 MiB, count=399993, average=74 B
top = end.statistics('lineno')                               # size=14.5 MiB, count=199987, average=76 B

for stat in top[:10]:
    print(stat)
'''
E:/python_learn/如何为创建大量实例节省内存.py:16: size=14.5 MiB, count=199987, average=76 B
E:/python_learn/如何为创建大量实例节省内存.py:66: size=7837 KiB, count=100004, average=80 B
E:/python_learn/如何为创建大量实例节省内存.py:65: size=6274 KiB, count=100001, average=64 B
E:/python_learn/如何为创建大量实例节省内存.py:68: size=448 B, count=1, average=448 B
G:\python3.6.5\lib\tracemalloc.py:522: size=64 B, count=1, average=64 B
'''

3.Python中的with语句
上下文管理器协议

try:
    f = open('test.txt', 'w')
    # print('hello')
    raise KeyError
except KeyError as e:
    print("Key Error")
# else:
except IndexError as e:
    print('IndexError')
    f.close
except Exception as e:
    print('Except')
    f.close
finally:
    # 不管有没有异常都会运行
    print('end')
    f.close


# with open('test.txt', 'w') as f:
#     f.read()


# 上下文管理器     简化异常处理
class Sample(object):
    
    # 获取资源
    def __enter__(self):
        print('1')
        return self
    
    # 释放资源
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)                            # <class 'AttributeError'>    异常类
        print(exc_val)                             # 'Sample' object has no attribute 'dem'    异常值
        print(exc_tb)                              # <traceback object at 0x00000221B953C2C8>  追踪信息
        print('3')
    
    def demo(self):
        print('2')


with Sample() as sample:  # AttributeError: __enter__
    sample.demo()                                  # 当调用的方法错误时候,错误信息才会被追踪  有输出值

contextlib简化上下文管理器

import contextlib

@contextlib.contextmanager                                       # 装饰器
def file_open(filename):
    print("file open")                         # yield上面的语句相当于__enter__函数
    yield {}                                   # 返回空字典       file operation   对文件的操作
    print("file end")                          # yield下面的语句相当于__exit__函数

with file_open('lib1.py') as f:
    print("file operation")

4.如何创建可管理的对象属性
在面向对象编程中,我们把方法看做对象的接口。直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如访问属性简洁。

# a.get_key()       # 访问器
# a.set_key()       # 设置器

# 创造一个形式上是:属性访问
#        实际上是:调用方法
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('TypeError')
        self.age = age
    
    R = property(get_age,set_age)
    #   property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
    
    
    
    @property      # 装饰器   get方法实现
    def S(self):
        return self.age
    
    @S.setter     # set方法
    def S(self, age):
        if not isinstance(age, int):
            raise TypeError('TypeError')
        self.age = age
    '''上面的源码
    class C(object):
            @property
            def x(self):
                "I am the 'x' property."
                return self._x
            @x.setter
            def x(self, value):
                self._x = value
            @x.deleter
            def x(self):
                del self._x
    
    '''
        
a = A(18)

# 文件读取的  str
a.age = '20'

a.set_age(20)
print(a.get_age())

a.R = 20                                  # 形式上是属性访问,实际上是方法调用
print(a.R)

a.S = 22                                 # 设置值和获取值会自动识别方法
print(a.S)

5、让类支持比较操作
有时我们希望自定义类的实例间可以使用,<,<=,>,>=,==,!=符号进行比较

In [11]: a=2

In [12]: b=1

In [13]: a>b
Out[13]: True

In [14]: a.__lt__(b)       # 小于    比较大小实际调用的方法
Out[14]: False

In [15]: a.__ge__(b)       # 大于等于
Out[15]: True

In [16]: a<b
Out[16]: False

In [17]:  s1='abc'

In [18]: s2='abd'

In [19]: s1>s2                       # 比较的是ASCII码
Out[19]: False

In [20]: ord('c')>ord('d')          # 比较的是ASCII码
Out[20]: False

In [21]: s1.__ge__(s2)
Out[21]: False

# 集合比较的是是否包含的关系,不是大小
In [22]: {1,2,3} > {4}
Out[22]: False

In [23]: {1,2,3} < {4}
Out[23]: False

In [24]: {1,2,3} == {4}
Out[24]: False

In [25]: {1,2,3} > {1}
Out[25]: True

In [26]: {1,2,3} > {1,3}
Out[26]: True

In [27]: {1,2,3} > {1,4}
Out[27]: False

In [28]: {1,2,3} > {2,3,4}
Out[28]: False

例如,有一个矩形的类,比较两个矩形的实例时,比较的是他们的面积

from functools import total_ordering  # 引入方法
from abc import ABCMeta,abstractmethod                # 抽象基类运用

import abc                                            # 直接引入abc也行


class Shape(metaclass=ABCMeta):
    @abstractmethod
    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, w, h):
        self.w = w
        self.h = h
    
    def area(self):
        return self.w * self.h
    
    def str(self):
        return "(%s, %s)" % (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(3, 4)

print(rect1.area() > rect2.area())  # 不是类之间的比较

print(rect1 < rect2)  # 类的比较
print(rect1 > rect2)  # 类的比较    内部自动转换成 rect2 < rect1

print(rect1 >= rect2)  # 类的比较  False
print(rect1 <= rect2)  # 类的比较  True
print(rect1 == rect2)  # 类的比较  False


import math


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

6.如何在环状数据结构中管理内存

In [30]: class BB:
    ...:     def __del__(self):
    ...:         print('del')
    ...:

In [31]: import weakref

In [32]: a = BB()

In [33]: a2 = weakref.ref(a)            # 弱引用   不占用引用计数,可以被直接回收

In [34]: a3 = a2()

In [35]: del a

In [36]: del a3
del

In [37]: del a2

弱引用在双向链表中的运用
双向循环链表

import weakref     # 弱引用

# 双向循环链表


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):
        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...')

发布了50 篇原创文章 · 获赞 9 · 访问量 2085

猜你喜欢

转载自blog.csdn.net/weixin_42118531/article/details/103794107
今日推荐