python类和对象详解_Python类与对象实例详解

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

问题1: 我们想定义一种新类型的元组,对于传入的可迭代对象,我们只保留其中int类型且值大于0的元素,例如:IntTuple([1,-1,'abc',6,['x','y'],3])==>(1,6,3),要求IntTuple是内置tuple的子类,如何实现?

解决方案:定义类IntTuple继承tuple,并实现new,修改实例化行为

new方法接受的参数虽然也是和init一样,但init是在类实例创建之后调用,而 new方法正是创建这个类实例的方法。new方法会返回所构造的对象,init则不会,在使用new返回对象的时候会隐式调用init函数。new函数必须以cls作为第一个参数,而init则以self作为其第一个参数

class IntTuple(tuple):

def __new__(cls, iterable):

g = (x for x in iterable if isinstance(x,int) and x > 0)

return super(IntTuple, cls).__new__(cls,g)

def __init__(self, iterable):

return super(IntTuple,self).__init__(iterable)

t = IntTuple([1,-1,'abc',6,['x','y'],3])

print t

输出:

(1, 6, 3)

2.如何为创建大量实例节省内存

问题2: 某网络游戏中,定义了玩家类Player(id,name,status,...),每有一个在线玩家,服务器内部则有一个Player的实例,当在线人数很多时,将产生大量实例,如何降低这些大量实例的内存开销

解决方案:定义类的slots属性,它是用来声明实例属性名字的列表

class Player(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 = Player('0001',"Jim")

p2 = Player2('0001',"jim")

print dir(p1)

print '\n'

print dir(p2)

print '\n'

print set(dir(p1)) -set(dir(p2))

输出:

['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'level', 'name', 'status', 'uid']

['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', 'level', 'name', 'status', 'uid']

set(['__dict__', '__weakref__'])

# 可以看到使用__slots__属性的类,比正常的少了__dict__,即不可以动态绑定属性,但是也减少了内存消耗,因为__dict__使用会占用内存

3.如何让对象支持上下文管理

问题3: 实现了一个telnet客户端的类TelnetClient,调用实例的start()方法启动客户端与服务器交互,交互完毕后需要调用cleanup方法关闭已连接的socket,以及将操作历史记录写入文件并关闭,能否让TelnetClient的实例支持上下文管理协议,从而替代手工调用cleanup()方法

from telnetlib import Telnet

from sys import stdin, stdout

from collections import deque

class TelnetClient(object):

def __init__(self, addr, port=23):

self.addr = addr

self.port = port

self.tn = None

def start(self):

self.tn = Telnet(self.addr, self.port)

self.history = deque()

# 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(user) + 1:])

def cleanup(self):

self.tn.close()

self.tn = None

with open(self.addr + '_history.txt', 'w') as f:

f.writelines(self.history)

client = TelnetClient('127.0.0.1')

print '\nstart'

client.start()

print '\ncleanup'

client.cleanup()

执行Python文件,输入用户名,密码登录telnet服务器,然后输入命令进行测试,然后ctrl + D退出,发现此时会生成127.0.0.1_history.txt文件,里面记录了操作的历史命令

AAffA0nNPuCLAAAAAElFTkSuQmCC

解决方案:

实现上下文管理协议,需定义实例的enter,exit方法,它们分别在with开始和结束时被调用

from telnetlib import Telnet

from sys import stdin, stdout

from collections 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(user) + 1:])

def __enter__(self):

self.tn = Telnet(self.addr, self.port)

self.history = deque()

return self

def __exit__(self, exc_type, exc_val, exc_tb):

self.tn.close()

self.tn = None

with open(self.addr + '_history.txt', 'w') as f:

f.writelines(self.history)

# with TelnetClient('127.0.0.1') as client的对象是 __enter__方法return返回的对象,当start()方法抛出异常时,仍然会执行__exit__里面的方法

with TelnetClient('127.0.0.1') as client:

client.start()

4.如何创建可管理的对象属性

问题4:在面向对象编程中,我们把方法看做对象的接口,直接访问对象的属性可能是不安全的,或设计上不够灵活,但是使用调用方法在形式上不如访问属性简洁,能否在形式上是属性访问,但实际上调用方法?

解决方案: 使用内置的property函数,Python内置的@property装饰器就是负责把一个方法变成属性调用

from math import pi

class Circle(object):

def __init__(self, radius):

self.radius = radius

def getRadius(self):

return self.radius

def setRadius(self, value):

if not isinstance(value, (int, long, float)):

raise ValueError('wrong type')

self.radius = float(value)

def getArea(self):

return self.radius ** 2 * pi

R = property(getRadius, setRadius)

c = Circle(3.2)

# 得到getRadius方法里的值

print c.R

# 使用setRadius设置值

c.R = 5.9

print c.R

输出:

3.2

5.9

5.如何让类支持比较操作

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

解决方案:比较符号运算符重载,需要实现以下方法lt和le和gt和eq和ne

使用标准库下的functools下的类装饰器total_ording 可以简化此过程

使用total_ordering装饰器的时候,类里面需要定义eq和大于gt或小于lt的一个方法

from functools import total_ordering

@total_ordering

class Rectangle(object):

def __init__(self, w, h):

self.w = w

self.h = h

def area(self):

return self.w * self.h

def __lt__(self, obj):

return self.area() < obj.area()

def __eq__(self, obj):

return self.area() == obj.area()

r1 = Rectangle(5, 3)

r2 = Rectangle(4, 4)

print r1 < r2

输出:

True

两个类的实例化进行比较,两个类里要有共同的方法area,都是比较面积

from functools import total_ordering

@total_ordering

class Rectangle(object):

def __init__(self, w, h):

self.w = w

self.h = h

def area(self):

return self.w * self.h

def __lt__(self, obj):

return self.area() < obj.area()

def __eq__(self, obj):

return self.area() == obj.area()

class Circle(object):

def __init__(self, r):

self.r = r

def area(self):

return self.r ** 2 * 3.14

r1 = Rectangle(5, 3)

r2 = Rectangle(4, 4)

c1 = Circle(3)

print r1 < c1

输出:

True

使用抽象基类的方式来进行比较两个类

from functools import total_ordering

from abc import ABCMeta, abstractmethod

@total_ordering

class Shape(object):

@abstractmethod

def area(self):

pass

def __lt__(self, obj):

if not isinstance(obj, Shape):

raise TypeError('obj is not shape')

return self.area() < obj.area()

def __eq__(self, obj):

return self.area() == obj.area()

class Rectangle(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 = Rectangle(5, 3)

r2 = Rectangle(4, 4)

c1 = Circle(3)

print c1 >= r1

输出:

True

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

问题6: 在某项目中我们实现了一些类,并希望能像静态类型语言那样(C,C++,Java)对它们的实例属性做类型检查

p = Person()

p.name = 'bob' # 必须是str

p.age = 18 # 必须是int

p.height = 1.83 # 必须是float

要求:

可以对实例变量名指定类型,赋予不正确类型时抛出异常

解决方案:使用描述符来实现需要类型检查的属性,分别实现get和set和delete方法,在set内使用isinstance函数做类型检查

class Attr(object):

def __init__(self, name, type_):

self.name = name

self.type_ = type_

def __get__(self, instance, cls):

return instance.__dict__[self.name]

def __set__(self, instance, value):

if not isinstance(value, self.type_):

raise TypeError('expected an %s' % self.type_)

instance.__dict__[self.name] = value

def __delete__(self, instance):

del instance.__dict__[self.name]

class Person(object):

name = Attr('name', str)

age = Attr('age', int)

height = Attr('height', float)

p = Person()

p.name = 'Bob'

print p.name

# 由于age定义的类型是int,故传递str会报错

p.age = '18'

输出:

Bob

Traceback (most recent call last):

File "/home/yangyang/train/shuxing.py", line 28, in

p.age = '18'

File "/home/yangyang/train/shuxing.py", line 13, in __set__

raise TypeError('expected an %s' % self.type_)

TypeError: expected an

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

问题7: 在python中,垃圾回收器通过引用计数来回收垃圾对象,但某些环状结构存在对象间的循环引用,比如树的父节点引用子节点,子节点也同时引用父节点.此时同时del掉引用父子节点,两个对象不能被立即回收.如何解决此类的内存管理问题?

解决方案: 使用标准库weakref,它可以创建一种能访问对象但不增加引用计数的对象

引用计数例子:

In [1]: class A(object):

...: def __del__(self):

...: print 'in A.__del__'

...:

In [2]: a = A()

In [3]: import sys

# 查看A()的引用计数,用sys.getrefcount()函数,比期望值多1

In [4]: sys.getrefcount(a)

Out[4]: 2

In [5]: a = 5

in A.__del__

循环引用例子:

class Data(object):

def __init__(self, owner, value):

# 这里引用了Node,因为Node引用的时候传入了自身

self.owner = owner

self.value = value

def __str__(self):

return "%s's data, value is %s" % (self.owner, self.value)

def __del__(self):

print "in Data.__del__"

class Node(object):

def __init__(self, value):

# 这里引用了Data

self.data = Data(value, self)

def __del__(self):

print "in Node.__del__"

node = Node(100)

del node

raw_input("wait....")

输出:

wait

解决循环引用例子

import weakref

class Data(object):

def __init__(self, value, owner):

self.value = value

self.owner = weakref.ref(owner)

def __str__(self):

return "%s's data, value is %s" % (self.owner(), self.value)

def __del__(self):

print "in Data.__del__"

class Node(object):

def __init__(self, value):

self.data = Data(value, self)

def __del__(self):

print "in Node.__del__"

node = Node(100)

del node

raw_input("wait....")

输出:

in Node.__del__

in Data.__del__

wait....

8.如何通过实例方法名字的字符串调用方法

问题8:某项目中,我们的代码使用了三个不同库中的图形类,Circle, Triangle,Rectangle,它们都有一个获取图形面积的方法,但方法名字不同,我们可以实现一个统一的获取面积的函数,使用每种方法名进行尝试,调用相应类的接口

解决方案:

方法一:使用内置函数getattr,通过名字在实例上获取方法对象,然后调用

方法二:使用标准库下operator下的methodcaller函数调用

# test.py

from lib1 import Circle

from lib2 import Triangle

from lib3 import Rectangle

def getArea(shape):

for name in ('area', 'getArea', 'get_area'):

f = getattr(shape, name, None)

if f:

return f()

shape1 = Circle(2)

shape2 = Triangle(3, 4, 5)

shape3 = Rectangle(6, 4)

shapes = [shape1, shape2, shape3]

print map(getArea, shapes)

输出:

[12.56, 6.0, 24]

#lib1.py

class Circle(object):

def __init__(self, r):

self.r = r

def area(self):

return self.r ** 2 * 3.14

# lib2.py

class Triangle(object):

def __init__(self, a, b, c):

self.a = a

self.b = b

self.c =c

def getArea(self):

a, b, c = self.a, self.b, self.c

p = (a + b + c)/2

area = (p * (p - a) * (p - b) * (p - c)) ** 0.5

return area

# lib3.py

class Rectangle(object):

def __init__(self, w, h):

self.w = w

self.h = h

def get_area(self):

return self.w * self.h

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值