Python高效编程实战---6、类与对象深度技术进阶

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

 # 将列表变成元组,且只保留大于0的整数
class IntTuple(tuple):
    def __new__(cls, iterable):
        g = (x for x in iterable if isinstance(x, int) and x > 0)  # isinstance()判断x是不是int整数类,
        return super(IntTuple, cls).__new__(cls, g)

    # def __init__(self, iterable):   # 默认调用父类的__init__()
    #     super(IntTuple, self).__init__(iterable)

print IntTuple([1, 2, 6, 'abc', -1, {'a': 5}, 7])   # 结果:(1, 2, 6, 7)

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

 class player1(object):
    def __init__(self, uid, name):
        self.uid = uid
        self.name = name


class player2(object):
    __slots__ = ['uid', 'name']  # 节约内存

    def __init__(self, uid, name):
        self.uid = uid
        self.name = name


p1 = player1('001', 'jim')
p2 = player2('001', 'jim')
p1.x = 100  # p1可以动态添加或删除属性,而p2不能
p1.y = 0
p1.__delattr__('x')  # 删除属性
print set(dir(p1)) - set(dir(p2))  # 结果 set(['y', '__dict__', '__weakref__'])  '__dict__'占用较大内存

import sys

print sys.getsizeof(p1.__dict__)  # 查看占用内存

三、如何让对象支持上下文管理

from telnetlib import telnetlib
from sys import stdin, stdout
from collecttions import deque


class TelnetClient(object):
    def __init__(self, addr, port=23)
        self.addr = addr
        self.port = port
        self.tn = None

    def start(self):
        # user
        t = self.tn.read_until('login:')
        stdout.write(t)
        user = stdin.readline()
        self.tn.write(user)

        # password
        t = self.tn.read_until('Password:')
        if t.startswith(user[:-1]): t = t[len(user) + 1:]
        stdout.write(t)
        self.tn.write(stdin.readline())

        t = self.tn.read.until('$ ')
        stdout.write(t)
        while True:
            uinput = stdin.readline()
            if not uinput:
                break
            self.history.append(uinput)
            self.tn.write(uinput)
            t = self.tn.read_until('$ ')
            stdout.write(t[len(uinput) + 1:])

    def __enter__(self):
        self.tn = Telnet(self.addr, self.port)
        self.history = deque()
        return self

        pass
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cn.close()
        self.cn = None
        with open(self.addr + '_history.txt', 'w') as f:
            f.writelines(self.history)

with TelnetClient('127.0.0.1') as client:
    client.start()

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

class Circle(object):
    def __init__(self,radius):
        self.radius = radius

    def getRadius(self):
        return round(self.radius,2)  # 保留两位小数

    def setRadius(self,value):
        if isinstance(value,(int,long,float)):  # 判断value是否是数字类
            self.radius=float(value)
        else:
            raise ValueError('wrong type')
    R = property(fget=getRadius,fset=setRadius,)  # property()为类创建可管理属性
c=Circle(3.5)
print c.radius

c.radius='abc' # 显然赋值不符合要求,但程序未能检测出
print c.radius

c.R=53.3473934   # 可以取值,也可以赋值
print c.R   # 可以管理,只保留两位小数 # 53.35
# c.R='abc'  # 程序就会报错

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

 # -*- coding: utf-8 -*-
from functools import total_ordering
from abc import ABCMeta, abstractmethod


@total_ordering  # 添加这个装饰器后,只需要重载__ltf__,__eq__ 两个比较函数,其它的比较函数装饰器会根据这两个判断生成。
class Shape(object):
    @abstractmethod
    def area(self):   # 定义,需要子类重载
        pass

    def __lt__(self, other):
        print 'in __lt__'
        if not isinstance(other, Shape):   # 判断比较的类是不是Shape的实例
            raise TypeError('other is not Shape')
        return self.area() < other.area()

    def __eq__(self, other):
        print 'in __eq__'
        if not isinstance(other, Shape):
            raise TypeError('other is not Shape')
        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


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

    def area(self):
        return self.r ** 2 * 3.14  # **表示乘方


r1 = Rect(6, 2)
r2 = Rect(2, 2)
c = Circle(1)

print c <= r2  # 要判断 r1

六、如何使用描述符对实例属性做类型检查

class Attr(object):
    def __init__(self, name, type_):
        self.name = name
        self.type_ = type_

    def __get__(self, instance, cls):  # 取值 
        print 'in __get__', self, instance, cls
        return instance.__dict__[self.name]

    def __set__(self, instance, value):  # 赋值,可以判断属性,以保证赋值不出错
        print 'in __set__', self, instance, value
        if not isinstance(value, self.type_):
            raise TypeError('is not format ')
        instance.__dict__[self.name] = value

    def __del__(self, instance):  # 重载del方法
        print 'in __del__'
        del instance.__dict__[self.name]

class Person(object):
    name = Attr('name', str)  # 指定类属性的类型
    age = Attr('name', int)
    height = Attr('height', float)

p = Person()
p.name = 'litaifa'
print p.name
p.name = 12  # 此处会报错

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

 import sys,weakref

class Data(object):
    def __init__(self,value,owner):
        self.value=value
        self.owner=weakref.ref(owner)   # 弱引用,不会增加引用计数,
        # self.owner=owner

    def __str__(self):
        return "%s's data,value is %s" % (self.owner(),self.value)  #  需要象函数一样调用owner()
    def __del__(self):
        print 'in Data.__del__ %s ' % (self.value)

class Node(object):
    def __init__(self,value):
        self.data=Data(value,self)  # 引用Data类
    def __del__(self):
        print 'in Node.__del__ '

node=Node(100)
print node.data
print sys.getrefcount(node)-1  # 返回node的引用计数
del node   # 只有当上面的引用计数为零时,才会调用析构函数__del__(),由于弱引用不增加引用计算,所以del后,会调用析构函数
raw_input("wait...")

# 结果:
# <__main__.Node object at 0x0000000003204668>'s data,value is 100
# 2
# in Node.__del__ 
# in Data.__del__ 100
# wait...

八、如何通过实例方法名字的字符串调用方法

from operator import methodcaller

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

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

class Circle(object):
    def __init__(self, r):
        self.r = r

    def get_area(self):
        return self.r ** 2 * 3.14

def getArea(shape):
    for name in ['area', 'get_area']:
        f = getattr(shape, name, None)
    if f:
        # 方法一:
        return f()
        # 方法二:
        return methodcaller(name)(shape)  # 相当于shape.name()

shapes = [Rect(2, 3), Circle(1)]
print map(getArea, shapes)  # map 对shapes的每个元素应用getArea(),然后返回一个新的列表

你可能感兴趣的:(python进阶)