文档章节

Python类与对象

听风的小猪
 听风的小猪
发布于 2017/05/16 13:58
字数 2595
阅读 637
收藏 25
点赞 3
评论 2

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文件,里面记录了操作的历史命令 输入图片说明

解决方案:

实现上下文管理协议,需定义实例的__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 <module>
    p.age = '18'
  File "/home/yangyang/train/shuxing.py", line 13, in __set__
    raise TypeError('expected an %s' % self.type_)
TypeError: expected an <type 'int'>

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

© 著作权归作者所有

共有 人打赏支持
听风的小猪

听风的小猪

粉丝 58
博文 38
码字总数 34058
作品 0
黄浦
程序员
加载中

评论(2)

s
sapling123
任重道远啊
FecShop
FecShop
撸起来:https://github.com/fancyecommerce/yii2_fecshop
1. Python3源码—内建对象

1.1. Python内的对象 Python中的类和实例都是通过Python内的对象来实现的。Python中已经预先定义了一些类型对象。这些内建类型对象通过实例化,可以创建内建类型对象的实例对象。 在Python中...

whj0709 ⋅ 06/06 ⋅ 0

Python是面向对象的语言吗?python编程

  Python虽然是解释型语言,但从设计之初就已经是一门面向对象的语言,对于Python来说一切皆为对象。正因为如此,在Python中创建一个类和对象是很容易的,当然如果习惯面向过程或者函数的写...

老男孩Linux培训 ⋅ 06/08 ⋅ 0

Python学习笔记二十四( 元类 )

class属性 class属性可以查看对象的类型. Person 类的实例对象person 的类型时Person 类类型, Python 是面向对象的语言, 那么Person 的类对象的类型又是什么? Person / int / str 的类对象的...

DragonFangQy ⋅ 05/27 ⋅ 0

Python高级编程和异步IO并发编程

Python高级编程和异步IO并发编程 网盘地址:https://pan.baidu.com/s/1eB-BsUacBRhKxh7qXwndMQ 密码: tgba 备用地址(腾讯微云):https://share.weiyun.com/5Z3x9V0 密码:7cdnb2 针对Pytho...

人气王子333 ⋅ 04/23 ⋅ 0

python3.x与python2.x的区别汇总

python3.x与python2.7.x都是比较流行的版本,虽然建议现在的初学者开始学习python3.x的版本,但是还有很多的工程使用的是python2.7.x版本。观看代码的时候难免会出现一些问题。 在google上搜...

oldpan ⋅ 2017/10/10 ⋅ 0

12道必会的Python面试题,附详细讲解

无论是应聘Python方向的web开发,还是爬虫工程师,或是数据分析,还是自动化运维,都涉及到一些基础的知识!小编挑了一些Python的基础面试题,看看你能不能的答上来,也许面试的同学用的着!...

诸葛玥 ⋅ 06/16 ⋅ 0

Python面向对象编程之我见

面向对象基本概念 面向对象是一种编程范式。范式是指一组方法论。编程范式是一组如何组织代码的方法论。编程范式指的是软件工程中的一种方法学。 一些主流的编程范式: OOP - 面向对象编程 ...

bigstone2012 ⋅ 05/31 ⋅ 0

python2.x和python3.x的区别

Python的3.0版本,常被称为Python3000,或简称Py3k。相对于Python的早期版本,这是一个较大的升级。 为了不带入过多的累赘,Python3.0在设计的时候没有考虑向下相容。许多针对早期Python版本...

leejia1989 ⋅ 05/30 ⋅ 0

Python面向对象之运算符重载

运算符重载只是意味着在类方法中拦截内置的操作,也就是说当类的实例出现在内置操作中,Python自动调用我们的方法,并且我们的方法的返回值变成了相应操作的结果。 关于重载的关键知识点: ...

bigstone2012 ⋅ 06/08 ⋅ 0

6 个 Python 的日期时间库

在 Python 中有许多库可以很容易地测试、转换和读取日期和时间信息。 这篇文章是与 Jeff Triplett 一起合写的。 曾几何时,我们中的一个人(Lacey)盯了一个多小时的 Python 文档中描述日期和...

16% ⋅ 05/22 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Qt中的坑--QTreeWidget添加item 不能显示出来

QTreeWidget* pTree = ui.TreeCheckList; QTreeWidgetItem* item = new QTreeWidgetItem(pTree) ;item->setText ( 0, "test" );pTree->addTopLevelItem (item ); 原因是因为创建一个......

k91191 ⋅ 30分钟前 ⋅ 0

使用Guava的RateLimiter做限流

场景: 1. 在日常生活中,我们肯定收到过不少不少这样的短信,“京东最新优惠卷…”,“天猫送您…”。这种类型的短信是属于推广性质的短信。这种短信一般群发量会到千万级别。然而,要完成这...

wind2012 ⋅ 31分钟前 ⋅ 0

QSlider重新enterEvent

#ifndef DIALOG_H#define DIALOG_H#include <QDialog>namespace Ui {class Dialog;}class Dialog : public QDialog{ Q_OBJECTpublic: explicit Dialog(QW......

xxdd ⋅ 31分钟前 ⋅ 0

生产环境redis备份与恢复

生产环境redis备份与恢复 Tyrant0532 0人评论 1563人阅读 2018-02-01 20:34:10 redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理。生产中我们主...

rootliu ⋅ 33分钟前 ⋅ 0

nginx中出现403forbidden错误

nginx “403 Forbidden” 错误 出现这个错误一般是因为以下原因: 网站禁止特定的用户访问所有内容,例:网站屏蔽某个ip访问。 访问禁止目录浏览的目录,例:设置autoindex off后访问目录。 ...

河图再现 ⋅ 34分钟前 ⋅ 0

上海云栖:金融政企行业的CDN最佳实践

摘要: 在刚刚结束的上海云栖大会飞天技术汇分论坛上,阿里云视频云产品架构师罗小飞进行了《阿里云CDN——面向金融政企的CDN最佳实践》主题分享,为上海的嘉宾介绍CDN的解决方案与技术服务体...

猫耳m ⋅ 39分钟前 ⋅ 0

docker 基本操作

docker介绍 Docker项目提供了构建在Linux内核功能之上,协同在一起的的高级工具。其目标是帮助开发和运维人员更容易地跨系统跨主机交付应用程序和他们的依赖。Docker通过Docker容器,一个安全...

haoyuehong ⋅ 40分钟前 ⋅ 0

上海云栖:金融政企行业的CDN最佳实践

摘要: 在刚刚结束的上海云栖大会飞天技术汇分论坛上,阿里云视频云产品架构师罗小飞进行了《阿里云CDN——面向金融政企的CDN最佳实践》主题分享,为上海的嘉宾介绍CDN的解决方案与技术服务体...

阿里云云栖社区 ⋅ 42分钟前 ⋅ 0

安装与配置hadoop

一、CentOS7安装 java8,参考centos7.0 安装java1.8,tomcat 二、安装hadoop 版本V3.03 1、下载并解压hadoop # mkdir /usr/local/app# mkdir /usr/local/app/hadoop# cd /usr/local/app/had......

iturtle ⋅ 44分钟前 ⋅ 0

Idea设置Serializable自动生成

File --> Settings --> Editor --> Inspections ->Serialization issues,在该项下找到“Serializable class without 'serialVersionUID' ”并勾选...

Gmupload ⋅ 47分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部