本章主题
引言
面向对象编程类
实例
绑定与方法调用子类,派生和继承内建函数
定制类
私有性
授权与包装
新式类的高级特性相关模块
调用一个方法:定义类(类中定义方法);创建实例;用实例调用方法
python中的类 可仅作名称空间----像 Pascal 中的记录集(records)和 C 语言中的结构体(structures)一样的特性。
在 Python 中,方法定义在类定义中,但只能被实例所调用。----结合兔子工厂图理解
class MyDataWithMethod(object): # 定义类
def printFoo(self): # 定义方法
print 'You invoked printFoo()!'
>>> myObj = MyDataWithMethod() # 创建实例
>>> myObj.printFoo() # 现在调用方法
You invoked printFoo()!
一个特殊的方法__init__()--------类似构造器,可以 认为一个构造器仅是一个特殊的方法,它在创建一个新的对象时被调用。
这就是实例化调用,它会自动调用__init__()。self 把实例对象自动传入__init__()。你可以 在脑子里把方法中的 self 用实例名替换掉。
创建子类
EmplAddrBookEntry。Python 中,当一个类被派生出来,子类继 承了基类的属性,所以,在上面的类中,我们不仅定义了__init__(),updatEmail()方法,而且 EmplAddrBookEntry 还从 AddrBookEntry 中继承了 updatePhone()方法。
每个子类最好定义它自己的构造器,如果子 类重写基类的构造器,基类的构造器就不会被自动调用了--这样,基类的构造器就必须显式写出 才会被执行,像我们上面那样,⚠️用 AddrBookEntry.__init__()设置名字和电话号码。
命名类、属性和方法
⚠️类-----大写字母开头class ClassName(object) ,如 EmplAddrBookEntry、AddrBookEntry ;
数据值---名字命名,如 像“name”,“phone;
方法----谓词(动词+对象)命名,如 “updatePhone”或“update_phone”,即常说的“混合记法(mixedCase)”或“骆驼记法(camelCase)”;
函数-----def functionName(args):
创建函数--在文件中(方法定义在类中)
创建类
类体=所有声明语句+类成员定义+数据属性和函数组成。
请注意 Python 并不支持纯虚函数(像 C++)或 者抽象方法(如在 JAVA 中),这些都强制程序员在子类中定义方法。作为替代方法,你可以简单地 在基类方法中引发 NotImplementedError 异常,这样可以获得类似的效果。
什么是属性呢?
属性就是属于另一个对象的数据或者函数元素,可以通过我们熟悉的句点属性 标识法来访问。一些 Python 类型比如复数有数据属性(实部和虚部),而另外一些,像列表和字典, 拥有方法(函数属性)。
如何知道一个类有哪些属性? 通过函数或属性
--使用dir()内建函数dir(MyClass) 返回的是对象的属性的名字列表;
--访问类的字典属性__dict__,这是所有类都具备的特殊属性之一。MyClass.__dict__,返回的是字典,是属性名和属性值的键值对
python支持模块间对类继承,通过特殊属性__module__定位类的位置
__init__() "构造器"方法
一旦对象创建了,Python 检查是否实现了 __init__()方法。默认情况下,如果没有定义(或覆盖)特殊方法__init__(),对实例不会施加任 何特别的操作.任何所需的特定操作,都需要程序员实现__init__(),覆盖它的默认行为。
⚠️构造器是最早可以设置实例属性的地方,因为__init__()是实例创建后第一个被调用的方法。__init__()就不应当返回任何对象(应当为 None)
__new__() “构造器”方法
需要一种途径来实例化不可变对象,比如,派生字符 串,数字,等等。在这种情况下,解释器则调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例 化操作时生成的。__new__()会调用父类的__new__()来创建对象(向上代理)。
__del__() "解构器"方法
这个函数要直到该实例对象所有的引用都被清除掉后才会执行。Python 中的解构器是在实例释放前提供特殊处理功能的方法,它们通常没有被实现,因为实例很少 被显式释放。
Python 类的优秀特性:能够在“运行时”创建实例属性;
⚠️使用类属性来修改自身(不是实例属性)
绑定和方法调用
方法仅仅是类内部定义的函数。(这意味着方法是类属性而不 是实例属性)。方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是 绑定到那个实例了。没有实例时方法就是未绑定的。
self 是什么?
self 变量用于在类实例方法中引用方法所绑定的实例。因为方法的实例在任何方法调用中总是 作为第一个参数传递的,self 被选中用来代表实例。你必须在方法声明中放上 self(你可能已经注 意到了这点),但可以在方法中不使用实例(self)。如果你的方法中没有用到 self , 那么请考虑创建 一个常规函数,除非你有特别的原因。毕竟,你的方法代码没有使用实例,没有与类关联其功能, 这使得它看起来更像一个常规函数。在其它面向对象语言中,self 可能被称为 this。
创建静态方法和类方法----staticmethod()和 classmethod()内建函数
通过类或者实例对象去调用 静态方法和 类方法
创建子类 如果没有任何继承,可以使用 object 作为父类的名字
继承特性
C 没有声明__init__()方法,然而在类 C 的实例 c 被创建时,还是会有输出信息。原因在于 C 继 承了 P 的__init__()。__bases__元组列出了其父类 P。需要⚠️的是文档字符串对类,函数/方法, 还有模块来说都是唯一的,所以特殊属性__doc__不会从基类中继承过来。
继承覆盖(Overriding)---如何调用那个被我覆盖的基类方法--在子类的重写方法里显式地调用基类方法
在这个(未绑定)方法调用中我们显式地传递了 self. 一个更好的办法是使用 super() 内建方法:
⚠️在 JAVA 中,不管程序员如何处理,子类构造器都会去调 用基类的的构造器。
多重继承
类、实例和其他对象的内建函数
issubclass() 布尔函数判断一个类是另一个类的子类或子孙类。语法: issubclass(sub, sup)
isinstance() 布尔函数在判定一个对象是否是另一个给定类的实例 语法: isinstance(obj1, obj2)
hasattr(), getattr(),setattr(), delattr()
*attr()系列函数可以在各种对象下工作,不限于类(class)和实例(instances)
dir()与 __dict__区别:__dict__是字典结构,dir()是列表结构 ( dictionary字典 direct直接关系)
super()可以简化搜索一个合适祖先的任务,并且在调用它时,替你传入实例或类型对象。
vars()内建函数与 dir()相似,只是给定的对象参数都必须有一个__dict__属性。vars()返回一 个字典,它包含了对象存储于其__dict__中的属性(键)及值。
*****用特殊方法定制类******
两个特殊方法可以分别作为构造器和析够器的功能,分别名为 __init__()和__del__()。它们中的一些有预定 义的默认行为,而其它一些则没有,留到需要的时候去实现。这些特殊方法是 Python 中用来扩充 类的强有力的方式。它们可以实现:
模拟标准类型
重载操作符
举例--数值定制,即时间格式化显示
举例--实现__add__()特殊方法
def __add__(self, other):
return self.__class__(self.hr + other.hr, self.min + other.min)
和正常情况下一样,新的对象通过调用类来创建。唯一的不同点在于,在类中,你一般不直接 调用类名,
而是使用 self 的 __class__属性,即实例化 self 的那个类,并调用它。
举例 定制__str()__方法,以覆盖默认的行为: ⚠️print(使用 str())、字符串对象表示(使用 repr())
def __str__(self):
return str(self.value)
或
def __str__(self):
return '%.2f' % self.value
为了可以让__repr__()和__str__()的输出一致,让__repr__()作为__str__()的一个别名: __repr__ = __str__
随机序列迭代器
创建迭代器对象------⚠️一个实现了 __iter__() 和 next() 方法的类可以作为迭代器使用.
🐷在 Python 中要将某一类型的变量或者常量转换为字符串对象通常有两种方法,即str()
或者 repr()
重构__repr__和__str__区别: 把一个类的实例变成 str__repr__
和__str__
这两个方法都是用于显示的,__str__
是面向用户的,而__repr__
面向开发者.想要直接输出对象(面向开发者)和使用 print 输入对象都显示的是友好提示除了重构__repr__
,还要重构__str__
.
self.__class__.__name__ 获取实例所在类的类名
多类型类定制----举例 重构➕
私有化
Java方法使用对比
13.18 练习
13-1. 程序设计。请列举一些面向对象编程与传统旧的程序设计形式相比的先进之处。
易维护,增加代码重复利用率,效率高、易扩展
13-2. 函数和方法的比较。函数和方法之间的区别是什么?
函数是一段代码。通过名字调用,它能将一些数据传递进去进行处理,然后返回一些数据。也可以没有返回值
方法也是一段代码,也通过名字来调用,但它跟一个对象相关联,方法和函数大致上是相同的,但有2个主要的不同之处:
1.方法中的数据是隐式传递的
2.方法可以操作类内部的数据
13-3. 对类进行定制。写一个类,用来将浮点数值转换为金额。在本练习里,我们使用美国货币,但读者也可以自选任意货币。
import re
class MoneyFmt(object):
def __init__(self, value=0.0): #构造器
self.mvalue=float(value)
def dollarize(self):
val = round(self.mvalue, 2)
strval = str(val)
if strval.startswith("-"):
strval=strval[1:]
else:
strval = strval
strval = str(strval).split('.')[0] #取第一个.之前的内容
strlen = len(strval)
num = strlen%3
ele1 = strval[:num]
ele2 = strval[num:]
l = re.findall(r'.{3}', ele2)
newstr = ele1
for i in l:
if num == 0:
newstr = str(i)
num +=1
else:
newstr = newstr+','+str(i)
return '$'+newstr+'.'+str(val).split('.')[1] #取.后内容
def update(self, newvalue=None): #允许修改
if newvalue is not None:
self.mvalue = float(newvalue)
def __repr__(self): #显示为浮点数 直接打印变量
return repr(self.mvalue)
# repr = str
def __str__(self): #格式化显示 print函数
if self.mvalue>0:
sign =''
else:
sign ='-'
return sign+self.dollarize()
def __nonzero__(self):
if self.mvalue == 0:
return False
else:
return True
#Split()[0] 函数详解
# 解析:
# str.split(“o”)[0]得到的是第一个o之前的内容
# str.split(“o”)[1]得到的是第一个o和第二个o之间的内容
# str.split(“o”)[3]得到的是第三个o后和第四个o前之间的内容
# str.split("[")[0]得到的是第一个 [ 之前的内容
# 注意:[ ]内的数值必须小于等于split("")内分隔符的个数,否则会报错
# str.split("[")[1]. split("]")[0]输出的是 [ 后的内容以及 ] 前的内容
13-4. 用户注册。建立一个用户数据库(包括登录名、密码和上次登录时间戳)类(参考练习7-5和9-12),来管理一个系统,该系统要求用户在登录后才能访问某些资源。这个数据库类对用户进行管理,并在实例化操作时加载之前保存的用户信息,提供访问函数来添加或更新数据库的信息。在数据修改后,数据库会在垃圾回收时将新信息保存到磁盘。(参见__del__()).
from datetime import datetime
import shelve, os
class userDB(object):
def __init__(self,dbfile):
self.db={}
if os.path.exists(dbfile):
self.db=shelve.open(dbfile,'c') #shelve库 # 参数flag的取值范围: 'r':只读打开 'w':读写访问 'c':读写访问,如果不存在则创建 'n':读写访问,总是创建新的、空的数据库文件
self.dbfile=dbfile
self.flag=False
def __del__(self):
data=shelve.open(self.dbfile,'c')
data.update(self.db)
data.close()
def login(self,user,pwd):
if user not in self.db:
self.flag=False
elif self.db[user][0]==pwd:
self.db[user][1]=datatime.now()
self.flag=True
def deluser(self,user):
if self.flag:
self.db.pop(user)
else:
print('login first')
def updateuser(self,user,pwd):
if self.flag:
self.db[user]=[pwd,datetime.now()]
else:
print('login first')
13-5. 几何. 创建一个由有序数值对(x, y) 组成的Point 类,它代表某个点的X 坐标和Y 坐
标。X 坐标和Y 坐标在实例化时被传递给构造器,如果没有给出它们的值,则默认为坐标的原点。
#! /usr/env/bin python
# -*- coding:utf-8 -*-
class Point(object):
def __init__(self,x=0,y=0):
self.x=x
self.y=y
def __str__(self):
return '(%d,%d)' % (self.x, self.y)
if __name__=='__main__':
p=Point(3,4)
print(p)
print("p的x坐标:%d" % p.x)
print("p的y坐标:%d" % p.y)
13-6. 几何. 创建一个直线/直线段类。除主要的数据属性:一对坐标值(参见上一个练习)外,
它还具有长度和斜线属性。你需要覆盖__repr__()方法(如果需要的话,还有__str__()方法),使得
代表那条直线(或直线段)的字符串表示形式是由一对元组构成的元组,即,((x1, y1), (x2, y2)).
总结:
__repr__ 将直线的两个端点(始点和止点)显示成一对元组
length 返回直线段的长度 - 不要使用"len", 因为这样使人误解它是整数。
slope 返回此直线段的斜率(或在适当的时候返回None)
from math import sqrt
class Line(object):
def __init__(self, x1=0, y1=0, x2=0, y2=0):
self.x1=x1
self.y1=y1
self.x2=x2
self.y2=y2
self.length = 0
self.slope = 0
def getLength(self):
self.length= sqrt((self.x1-self.x2)**2+(self.y1-self.y2)**2)
return self.length
def getSlope(self):
if self.length==0:
self.slope==None
elif (self.x1-self.x2)==0 or (self.y1-self.y2)==0:
self.slope==None
else:
self.slope=(self.y1-self.y2)/(self.x1-self.x2)
return self.slope
def __str__(self):
return "((%d,%d),(%d,%d))" % (self.x1,self.y1,self.x2,self.y2)
def __repr__(self):
repr=str
if __name__=='__main__':
L=Line(4,10,2,1)
print(L)
print('length is: %f' % L.length)
print ('slope is: %f' % L.slope)
13-7. 数据类。提供一个time 模块的接口,允许用户按照自己给定时间的格式,比如:
“MM/DD/YY,” “MM/DD/YYYY,” “DD/MM/YY,” “DD/MM/ YYYY,” “Mon DD, YYYY,” 或是标准
的Unix 日期格式:“Day Mon DD, HH:MM:SS YYYY” 来查看日期。你的类应该维护一个日期值,并
用给定的时间创建一个实例。如果没有给出时间值,程序执行时会默认采用当前的系统时间。还包括另外一些方法:
update() 按给定时间或是默认的当前系统时间修改数据值
display() 以代表时间格式的字符串做参数,并按照给定时间的格式显示:
'MDY' ==> MM/DD/YY
'MDYY' ==> MM/DD/YYYY
'DMY' ==> DD/MM/YY
'DMYY' ==> DD/MM/YYYY
'MODYY' ==> Mon DD, YYYY
如果没有提供任何时间格式,默认使用系统时间或ctime()的格式。附加题: 把这个类和练习6-15 结合起来。
#! /usr/env/bin python
# -*- coding:utf-8 -*-
# time.time()得到的float类型的秒数
# localtime() 函数类似gmtime(),作用是格式化时间戳为本地的时间
# strftime() 函数接收 时间元组,并返回 可读字符串表示的当地时间,格式由参数format决定。
# ctime() 函数把一个时间戳(按秒计算的浮点数)转化为time.asctime()的形式.它的作用相当于 asctime(localtime(secs)). 如果参数未给或者为None的时候,将会默认time.time()为参数
# asctime() 函数接受时间元组并返回一个可读的形式为"Tue Dec 11 18:07:14 2008"(2008年12月11日 周二18时07分14秒)的24个字符的字符串
import time
class TimeFormat(object):
def __init__(self,t=time.time()):
self.mytime=t
def update(self, t=time.time()):
self.mytime=t
def display(self, formtime=None):
fmt={}
fmt['MDY'] = '%m/%d/%y'
fmt['MDYY'] = '%m/%d/%Y'
fmt['DMY'] = '%d/%m/%y'
fmt['DMYY'] = '%d/%m/%Y'
fmt['MODYY'] = '%b %d,%Y'
if formtime in fmt:
return (time.strftime(fmt[formtime],time.localtime(self.mytime)))
if __name__ == '__main__':
tf = TimeFormat()
print(tf.display())
print(tf.display('MDY'))
print(tf.display('MDYY'))
print(tf.display('DMY'))
print(tf.display('DMYY'))
print(tf.display('MODYY'))
tf.update(time.time()+60)
print(tf.display())
# 执行结果如下:
# Tue Sep 8 16:37:43 2020
# 09/08/20
# 09/08/2020
# 08/09/20
# 08/09/2020
# Sep 08,2020
# Tue Sep 8 16:38:43 2020
13-8. 堆栈类。一个堆栈(Stack)是一种具有后进先出(last-in-first-out,LIFO)特性的数
据结构。你的类中应该有push()方法(向堆栈中压入一个数据项)和pop()方法(从堆栈中移出一个数据项)。还有一个叫isempty()的布尔方法,如果堆栈是空的,返回布尔值1,否则返回0;一个名叫peek()的方法,取出堆栈顶部的数据项,但并不移除它。
注意,如果你使用一个列表来实现堆栈,那么pop()方法从Python1.5.2 版本起已经存在了。那就在你编写的新类里,加上一段代码检查pop()方法是否已经存在。如果经检查pop()方法存在,就调用这个内建的方法;否则就执行你自己编写的pop()方法。你很可能要用到列表对象;如果用到它时,不需要担心实现列表的功能(例如,切片)。只要确保你写的堆栈类能够正确实现上面的两项功能就可以了。你可以用列表对象的子类或自己写个类似列表的对象,请参考示例6.2.
#! /usr/env/bin python
# -*- coding:utf-8 -*-
# 实现一个堆栈类,类中应该有push()和pop()方法,还有一个isempty()方法,如果堆栈是空的,返回布尔值1,否则返回0。
class Stack(object):
def __init__(self, l=[]):
self.l = l
def isempty(self):
if len(self.l) == 0:
return 1
return 0
def push(self,element):
print('pushed [', element,']')
self.l.append(element)
def pop(self):
if self.isempty():
print ('can not pop from an empty stack!')
else:
print ('removed [', self.l.pop(), ']')
def peek(self):
if self.isempty():
print('can not peek from an empty stack!')
else:
return self.l[0]
def viewstack(self):
print (self.l)
if __name__=='__main__':
s=Stack()
s.viewstack()
s.pop()
s.viewstack()
s.push(2)
s.viewstack()
13-9. 队列类。一个队列(queue)是一种具有先进先出(first-in-first-out,FIFO)特性的数据结构。一个队列就像是一行队伍,数据从前端被移除,从后端被加入。这个类必须支持下面几种方法:
enqueue() 在列表的尾部加入一个新的元素
dequeue() 在列表的头部取出一个元素,返回它并且把它从列表中删除。
class Queue(object):
def __init__(self, l=[]):
self.l=l
def enqueue(self,element):
print ('enter queue [', element, ']')
self.l.append(element)
def dequeue(self):
if self.l:
print ('removed [', self.l.pop(0),']')
else:
print ('can not pop from an empty queue')
def viewstack(self):
print (self.l)
if __name__=='__main__':
q=Queue([1,2,3])
q.viewstack()
q.enqueue(4)
q.viewstack()
q.dequeue()
q.viewstack()
q.dequeue()
q.viewstack()
13-10. 堆栈和队列。编写一个类,定义一个能够同时具有堆栈(FIFO)和队列(LIFO)操作行为的数据结构。这个类和Perl 语言中数组相像。需要实现四个方法:
shift() 返回并删除列表中的第一个元素,类似于前面的dequeue()函数。
unshift() 在列表的头部"压入"一个新元素
push() 在列表的尾部加上一个新元素,类似于前面的enqueue()和push()方法。
pop() 返回并删除列表中的最后一个元素,与前面的pop()方法完全一样。
class FifoLifo(object):
def __init__(self, l=[]):
self.l=l
def shift(self):
"""删除头"""
if self.l:
print ('removed [', self.l.pop(0),']')
else:
print('can not pop from an empty queue')
def unshift(self,element):
"""插入头 insert"""
print ('inserted [', element, ']')
self.l.insert(0,element)
def push(self,element):
"""插入尾 append"""
print('pushed [', element, ']')
self.l.append(element)
def pop(self):
"""删除列表最后一个元素"""
if self.l:
print ('deleted [', self.l.pop(), ']')
else:
print('can not pop from an empty stack')
def viewstack(self):
print(self.l)
if __name__=='__main__':
f=FifoLifo([1,2,3])
f.viewstack()
f.shift()
f.unshift(9)
f.viewstack()
13-11. 电子商务。
你需要为一家B2C(商业到消费者)零售商编写一个基础的电子商务引擎。你需要写一个针对顾客的类User, 一个对应存货清单的类Item, 还有一个对应购物车的类叫Cart. 货物放到购物车里,顾客可以有多个购物车。同时购物车里可以有多个货物,包括多个同样的货物。
class Item(object):
"""货品价格清单"""
def __init__(self, product, price):
self.product = product
self.price = price
def __str__(self):
return '(%s, %.2f)' % (self.product, self.price)
__repr__=__str__ #"""不加这行,Item无法打印"""
class Cart(object):
"""购物车 构造函数 属性"""
def __init__(self, cartname):
self.cartname=cartname
self.cartlist={} #??? 函数?方法?静态变量?
def appenditem(self, item, count=1):
"""方法:增加数量"""
if item not in self.cartlist: #物品在不在购物车列表?
self.cartlist[item]=count
else:
self.cartlist[item]+=count # 物品1:5,物品2:1 --> 产品 价格:数量
def showcart(self):
"""方法:查看"""
for i in self.cartlist:
print(self.cartname, i, self.cartlist[i]) #cartlist --key, cartlist[item]---value
def deleteitem(self, item, count=1):
"""方法:减少数量"""
if item in self.cartlist and self.cartlist[item]>=count:
self.cartlist[item]-=count
if self.cartlist[item]==0:
self.cartlist.pop(item)
class User(object):
"""顾客"""
def __init__(self, name):
self.name = name
self.userdb=[]
def appendcart(self, cart):
self.userdb.append(cart.cartname)
def showuser(self):
print(self.name, self.userdb)
if __name__=='__main__':
i1=Item('Huawei', 15000)
i2=Item('iphone',7000)
print(i1,i2)
i1
c1=Cart('cart1')
c2=Cart('cart2')
c3=Cart('cart3')
c1.appenditem(i1,1)
c1.appenditem(i2,1)
c2.appenditem(i2,3)
c3.appenditem(i1,2)
c1.showcart()
c2.showcart()
c3.showcart()
u1=User('Amanda')
u2=User('Lina')
u1.appendcart(c1)
u2.appendcart(c2)
u2.appendcart(c3)
u1.showuser()
u2.showuser()
'''
((Huawei, 15000.00), (iphone, 7000.00))
('cart1', (Huawei, 15000.00), 1)
('cart1', (iphone, 7000.00), 1)
('cart2', (iphone, 7000.00), 3)
('cart3', (Huawei, 15000.00), 2)
('Amanda', ['cart1'])
('Lina', ['cart2', 'cart3'])
'''
13-12 聊天室. 你对目前的聊天室程序感到非常失望,并决心要自己写一个,创建一家新的因特网公司,获得风险投资,把广告集成到你的聊天室程序中,争取在6个月的时间里让收入翻五倍,股票上市,然后退休。但是,如果你没有一个非常酷的聊天软件,这一切都不会发生。 你需要三个类: 一个Message类,它包含一个消息字符串以及诸如广播、单方收件人等其他信息,一个User类, 包含了进入你聊天室的某个人的所有信息。为了从风险投资者那里拿到启动资金,你加了一个Room类,它体现了一个更加复杂的聊天系统,用户可以在聊天时创建单独的“聊天屋”,并邀请其他人加入。附加题: 请为用户开发一个图形化用户界面应用程序。
class Message(object):
def __init__(self,msg='',broadcast=False, froms='', to=''):
self.msg=msg
self.broadcast=broadcast
self.froms=froms
self.to=to
def __str__(self):
"""广播"""
if self.broadcast:
return 'message %s from %s to everyone' % (self.msg, self.froms)
else:
return 'message %s from %s to %s' % (self.msg, self.froms, self.to)
class User(object):
hear = {'everyone':''} #类变量
def __init__(self, name, sex, age):
self.name=name
self.sex=sex
self.age=age
def __del__(self):
self.__class__.hear.clear() #dict2.clear() # 删除dict2中所有的条目 ,>>>> for key in dict2: print 'key=%s, value=%s' % (key, dict2[key])
def __str__(self):
return 'user: %s, sex: %s, age: %s' % (self.name, self.sex, self.age)
def talk(self, msg='', to=''):
'''发送消息(广播)'''
if to=='everyone':
m=Message(msg,True,self.name)
User.hear['everyone']=m #'''把m赋值给公共变量-hear字典-的key对应-的value'''
elif to:
m=Message(msg,False,self.name,to)
User.hear[to]=m
else:
print ('receiver can not be empty')
def hearmsg(self):
'''显示消息'''
if self.name in User.hear:
print(User.hear[self.name])
elif User.hear['everyone']:
print(User.hear['everyone'])
else:
print 'no message for %s' % self.name
def talkroom(self, room, to='', msg=''):
if to == 'everyone':
m=Message(msg,True,self.name)
room.receive(m)
elif to:
m=Message(msg,False,self.name,to)
room.receive(m)
else:
print ('receiver can not be empty')
def hearroom(self, m):
print ('room %s' % m)
def createroom(self, name, count=3):
return Room(name, count)
#"""room广播"""
class Room(object):
def __init__(self, rname, count=3):
self.rname=rname
self.count=count
self.userlist =[]
def adduser(self,user):
if len(self.userlist)<= self.count:
self.userlist.append(user)
else:
print 'user number limits'
def receive(self,m):
if m.broadcast:
print 'room %s' %m
else:
for user in self.userlist:
if user.name in self.userlist:
if user.name==m.to:
user.hearroom(m)
if __name__=='__main__':
u1 = User('bob', 'male', 33)
u2 = User('jim', 'female', 31)
u3 = User('Tom', 'female', 31)
u1.talk('jim', 'hello')
# u1.talk('everyone', 'hello all')
u2.hearmsg()
u3.hearmsg()
room1 = u2.createroom('girls', 2)
room1.adduser(u2)
room1.adduser(u3)
# room1.adduser(u1)
u2.talkroom(room1, 'Tom', 'hello')
u3.talkroom(room1,'everyone','hello')