设为首页 收藏本站
查看: 1165|回复: 0

[经验分享] Python学习笔记整理(十六)类的设计

[复制链接]
发表于 2018-8-5 12:12:48 | 显示全部楼层 |阅读模式
  如何使用类来对有用的对象进行建模?
  一、Python和OOP
  Python和OOP实现可以概括为三个概念。
  继承
  继承是基于Python中属性查找(在X.name表达式中)
  多态
  在X.method方法中,method的意义取决于X的类型(类)
  封装
  方法和运算符实现行为,数据隐藏默认是一种惯例。
  封装指的是在Python中打包,也就是把实现的细节隐藏在对象接口之后。这并不代表有强制的私有性。封装可以让对象接口的现实
  出现变动时,不影响这个对象的用户。
  1、不要通过调用标记进行重载
  不要在同一个类中对同一个方法名定义两次,后面的会覆盖前面,也不要对对象类型进行测试。应该把程序代码写成预期的对象接口。而不是特定类型的数据类型。
  2、类作为记录
  通过类的实例来创建多个记录。
  3、类和继承:是“一个”关系 (is a)
  从程序员的角度来看,继承是由属性点号运算启动的,由此触发实例,类以及任何超类中变量名搜索。
  从设计师的角度看,继承是一种定义集合成员关系的方式:类定义了一组内容属性,可由更具体的集合(子类)继承和定制。
  子类和超类的继承是1对1的关系.
  PizzaRobot是一种Chef,Chef是一种Employee.以OOP术语来看,我们称这些关系为“是一个连接”(is a):机器人是个主厨,主厨是一个员工。
  class Employee:
  def __init__(self,name,salary=0):
  self.name=name
  self.salary=salary
  def giveRaise(self,percent):
  self.salary=self.salary+(self.salary*percent)
  def work(self):
  print self.name,"does stuff"
  def __repr__(self):
  return &quot;<Employee:name=%s,salary=%s>&quot; % (self.name,self.salary)
  class Chef(Employee):
  def __init__(self,name):
  Employee.__init__(self,name,5000)
  def work(self):
  print self.name,&quot;make food&quot;
  class Server(Employee):
  def __init__(self,name):
  Employee.__init__(self,name,40000)
  def work(self):
  print self.name,&quot;interface with customer&quot;
  class PizzaRobot(Chef):
  def __init__(self,name):#有点想不明白,既然继承就够了,为什么还要在这里构造
  Chef.__init__(self,name)    #Chef.__init__(self,name) =》Employee.__init__(self,name,5000)=>__init__(self,name,salary=0)
  def work(self):
  print self.name,&quot;make pizza&quot;
  if __name__=='__main__':
  bob=PizzaRobot('bob')
  print bob
  bob.work()
  bob.giveRaise(0.20)
  print bob;print
  # python employees.py
  <Employee:name=bob,salary=5000>
  bob make pizza
  <Employee:name=bob,salary=6000.0>
  理解有问题的地方
  class PizzaRobot(Chef):
  def __init__(self,name):#有点想不明白,既然继承就够了,为什么还要在这里构造,下面拿掉这里做对比
  Chef.__init__(self,name)
  #Chef.__init__(self,name) =》Employee.__init__(self,name,5000)=>__init__(self,name,salary=0) 连接过程
  def work(self):
  print self.name,&quot;make pizza&quot;
  下面拿掉PizzaRobot类中的def __init__(self,name):
  # python employees.py
  <Employee:name=bob,salary=5000>
  bob make pizza
  <Employee:name=bob,salary=6000.0
  结果一样,那这个函数还有什么必要做构造,直接继承就可以了
  导入进行其他测试
  >>> import employees
  >>> obj=employees.Employee(employees.Employee.__name__)
  >>> obj.work
  Employee does stuff
  if下面增加如下代码:
  for klass in Employee,Chef,Server,PizzaRobot:
  obj=klass(klass.__name__)
  obj.work()
  代码对应的结果
  #python employees.py
  Employee does stuff
  Chef make food
  Server interface with customer
  PizzaRobot make pizza
  4、类和组合:”有一个“关系 (has a)
  从程序员的角度来看,组合设计到把其他对象嵌入到容器对象内,并使其实现容器方法。
  对设计师来说,组合是另一种表示问题领域中的关系的方法。
  但是组合不是集合的成员关系,而是组件,也是整体的组成部分。
  组合也反映了个组成部分之间的关系,通常称为“有一个”(has a)关系。Python中,“组合”(聚合)就是指内嵌对象集合体。
  组合类一般都提供自己的接口,并通过内嵌的对象来实现接口。
  现在,我们有了员工,把他们放到披萨店。我们的披萨店是一个组合对象,有烤炉,也有服务员和主厨这些员工。当顾客来下单
  时,店里的组件就开始行动:服务员接下订单,主厨制作披萨等。。pizzashop.py模拟
  from employees import PizzaRobot,Server
  class Customer:
  def __init__(self,name):
  self.name=name
  def order(self,server):
  print self.name,&quot;oders from&quot;,server
  def pay(self,server):
  print self.name,&quot;pays for item to&quot;,server
  class Oven:
  def bake(self):
  print &quot;oven bakes&quot;
  class PizzaShop:
  def __init__(self):
  self.server=Server('Pat')
  self.chef=PizzaRobot('Bob')
  self.oven=Oven()
  def order(self,name):
  customer=Customer(name)
  customer.order(self.server)
  self.chef.work()
  self.oven.bake()
  customer.pay(self.server)
  if __name__==&quot;__main__&quot;:
  scene=PizzaShop()
  scene.order('lily')
  print '...'
  scene.order('kelly')
  # python pizzashop.py
  lily oders from <Employee:name=Pat,salary=40000>
  Bob make pizza
  oven bakes
  lily pays for item to <Employee:name=Pat,salary=40000>
  ...
  kelly oders from <Employee:name=Pat,salary=40000>
  Bob make pizza
  oven bakes
  kelly pays for item to <Employee:name=Pat,salary=40000>
  PizzaShop类是容器和控制器,其构造器会创建employees模块中员工类的实例,并将其嵌入。当模块的自我检查程序代码
  调用PizzaShop的order方法时,内嵌的对象会按顺序进行工作。注意:每份订单创建了Customer对象,而且把内嵌的Server
  对象传递给Customer方法,顾客是流动的,但是服务员是披萨店的组成部分。另外,员工也涉及到了继承关系,组合和继承
  是互补工具。
  if检测改写一下,通过参数传递客户给模块
  if __name__==&quot;__main__&quot;:
  try:
  customer=getargv[1]
  except:
  print &quot;please give argv as customer!&quot;
  else:
  scene=PizzaShop()
  scene.order(customer)
  print '...'
  # python pizzashop.py tom
  tom oders from <Employee:name=Pat,salary=40000>
  Bob make pizza
  oven bakes
  tom pays for item to <Employee:name=Pat,salary=40000>
  ...
  5、重访流处理器
  # vim streams.py
  class Processor:
  def __init__(self,reader,writer):
  self.reader=reader
  self.writer=writer
  def process(self):
  while 1:
  data=self.reader.readline()
  if not data:break #这里有错误是not
  data=self.converter(data)
  self.writer.write(data)  #这里有错误write
  def converter(self,data)
  assert 0,'converter must be defined
  # vim converter.py
  from streams import Processor
  from sys import stdout as output
  class Uppercase(Processor):
  def converter(self,data):
  return data.upper()
  if __name__==&quot;__main__&quot;:
  Uppercase(openfile('/etc/rc.conf'),output).process()
  错误from sys import open as openfile
  ofreebsd# python converter.py
  Traceback (most recent call last):
  File &quot;converter.py&quot;, line 2, in <module>
  from sys import open as openfile
  open是内置函数无需导入、
  6、类和持续性
  持续性:保证数据的持续性,将数据保存在文件或者数据库,继续保存。
  pickle和shelve模块和类实例结合起来使用效果很好,通过单个步骤存储到硬盘上。
  >>> from pizzashop import PizzaShop
  >>> import pickle
  >>> obj=PizzaShop()
  >>> obj.server
  <Employee:name=Pat,salary=40000>
  file=open('pizzashop_data','w')
  >>> pickle.dump(obj.server,file) #写入
  >>> file.close()
  # cat pizzashop_data
  (iemployees
  Server
  p0
  (dp1
  S'salary'
  p2
  I40000
  sS'name'
  p3
  S'Pat'
  p4
  读取
  >> objread=pickle.load(file)
  >>> print objread
  <Employee:name=Pat,salary=40000>ile=open('pizzashop_data','r')
  pickle机制把内存中的对象转换成序列化的字节流,可以保存在文件中。
  Shelve会自动把对象pickle生成按键读取的数据库,而此数据库导出类似于字典的接口.
  >>> from pizzashop import PizzaShop
  >>> obj=PizzaShop()
  >>> obj.server
  <Employee:name=Pat,salary=40000>
  >>> import shelve
  >>> dbase=shelve.open('datafile')
  >>> dbase['key']=obj.server 写入
  >>> dbase.sync
  <bound method DbfilenameShelf.sync of {'key': <Employee:name=Pat,salary=40000>}>
  # cat datafile.db
  >>> dbase['name']='diege' #写入
  >>> dbase.sync
  <bound method DbfilenameShelf.sync of {'name': 'diege', 'key': <Employee:name=Pat,salary=40000>}>
  >>> dbase.close()
  # cat datafile.db
  a?
  (?n} ¤?¤S'diege'
  p1
  .name(iemployees
  Server
  p1
  (dp2
  S'salary'
  p3
  I40000
  sS'name'
  p4
  S'Pat'
  p5
  读取
  >>> import shelve
  >>> shtest=shelve.open('datafile')
  >>> shtest['key']
  <Employee:name=Pat,salary=40000>
  >>> shtest['name']
  'diege'
  二、OOP和委托
  所谓的委托,通常就是指控制器对象内嵌其他对象,而把运算请求传给那些对象。控制器负责管理工作。
  在Python中,委托通常是以__getattr__钩子方法实现的,因为这个方法会拦截对不存在属性的读取,包装类(代理类)可以使用__getattr__把任意读取转发给包装的对象。包装类包有被包括对象的接口。而且自己也可以增加其他运算。
  class wrapper:
  def __init__(self,object):
  self.wrapped=object
  def __getattr__(self,attrname):        #__getattr__点号运算,这里重载内置getattr方法打印传入类执行的方法,并把属性请求传入给对象,使用对象默认的方法。委托
  print 'Trace:',attrname
  return getattr(self.wrapped,attrname)
  __getattr__会获得属性名称字符串。这个程序代码利用getattr内置函数,以变量名字符串从包裹对象取出属性:getattr(X,Z)
  就像X.N,只不过N是表达式,可以在运行时计算出字符串,而不是变量。getattr(X,Z)类似于X.__dict__[N].
  可以使用这个模块包装类的做法,管理人和带有属性的对象的存取:列表,字典甚至是类和实例。
  在这里wrapper类只是在每个属性读取时打印跟踪信息,【并把属性请求委托给嵌入的wrapped对象。(对象自己的方法)】
  >>> from trac import wrapper
  >>> x=wrapper([1,2,3])
  >>> x.append
  Trace: append
  <built-in method append of list object at 0x2850f7ec>
  >>> x.append(4)
  Trace: append
  >>> for n in x:print n
  ...
  Trace: __iter__
  1
  2
  3
  4
  >>> x.wrapped
  [1, 2, 3, 4]
  >>> x.__dict__
  {'wrapped': [1, 2, 3, 4]}
  >>> x=wrapper({&quot;a&quot;:1,&quot;b&quot;:2})
  >>> x.keys()
  Trace: keys
  ['a', 'b']
  >>> x.__dict__
  {'wrapped': {'a': 1, 'b': 2}}
  >>> x
  Trace: __repr__
  {'a': 1, 'b': 2}
  三、多重继承
  在class语句中,首行括号内可以列出一个以上的超类。当这么做时,就在使用所谓的多重继承:类和其实例继承了列出的所有超类的变量。搜索属性时,Python会由左到右搜索类首行中的超类,直到找到相符者。
  通常意义上讲,多重继承是模拟属于一个集合以上的对象的好办法,例如一个人可以是工程师,作家,音乐家。因为,可以继承这些集合的特性。
  多重继承最常见的用户是作为“混合”超类的通用方法。这类超类一般都称呼混合类:他们提供方法,可以通过继承将其加入应用类。
  >>> x.__class__
  <class trac.wrapper at 0x28503f8c>
  >>> x.__class__.__name__
  'wrapper'
  每个实例都有内置的__class__属性,引用了它所继承的类,而每个类都有__name__属性,用用了首行中的变量名,所以self.__class__.__name__是取出实例的类的名称
  >>> x.__class__.__module__
  'trac'
  >>> x.__module__
  'trac
  而是用self.__module__或 self.__class__.__module__则取出实例引用模块的名称
  内置id函数传回任意对象的地址(从定义上来将。这就是唯一的对象识别码),从而获得实例的内存地址。
  # vim mytool.py
  class Lister:
  def __repr__(self): #打印重载
  return (&quot;<Instance of %s,address %s:\n%s&quot; %
  (self.__class__.__name__,

  >  self.attrnames()) ) #抽象类
  def attrnames(self):
  result=''
  for attr in self.__dict__.keys():
  if attr[:2]=='__':
  result=result+&quot;\tname %s=<built-in>\n&quot; % attr
  else:
  result=result+&quot;\tname %s=%s\n&quot; % (attr,self.__dict__[attr])
  return result
  从这个类衍生的实例会在打印时自动显示器属性。
  >>> from mytool import Lister
  >>> dir(Lister)
  ['__doc__', '__module__', '__repr__', 'attrname']

  >>>>  ...     def __init__(self):
  ...             self.data1='food'
  ...
  >>> y=Diege()
  >>> y
  <Instance of Diege,address 676368300:
  name data1=food
  >
  将__repr_修改为__str__
  >>> y=Diege()
  >>> y
  >>> print y
  <Instance of Diege,address 676368300:
  name data1=food
  >
  Lister类对所写的任何类都有用:即时是已经有超类的类,这里就是多重继承方便之处:把Lister加到类首行的超类列表中(将其混合进来)
  就可以获得其__repe,同时依然继承现有的超类。
  # vim Tsmixin.py
  from mytool import Lister
  class Super:
  def __init__(self):
  self.data1='diege'
  class Sub(Super,Lister):
  def __init__(self):
  Super.__init__(self)
  self.data2=&quot;eggs&quot;
  self.data3=42
  if __name__=='__main__':
  X=Sub()
  print X
  # python Tsmixin.py
  <Instance of Sub,address 676367724:
  name data1=diege
  name data3=42
  name data2=eggs
  >
  如果稍后你决定扩展Lister的__repr__,也要打印实例继承的所有类属性,那也很安全。因为这是继承的方法,修改Lister.__repr__会自动更新每个导入类,
  并将显示器混合进来的子类的情况。
  总之,混合类相当于模块:可用户在各种客户端的方法包。以下是Lister用在不同类实例上,采用单个继承模式的情况。

  >>>>  ...     pass
  ...
  >>> t=x()
  >>> t
  <Instance of X,address 676380940:
  >
  >>> t
  <Instance of X,address 676380940:
  name a=1
  name c=3
  name b=2
  >
  OOP其实就是代码重用,而混合是强大的工具。多重继承是高级功能,如果用的过度或太随意,就变得很复杂。
  四、类是对象:通用对象的工厂
  类是对象,因此它很容易在程序中进行传递,保存在数据库结构中。也可以把类传给产生任意种类对象的函数。这类函数在OOP设计领域偶尔称为工厂。
  工厂式的函数或程序代码,在一些情况下很方便,因为他们可以让我们取出并传入没有预先在程序代码中硬编码的类。实际上,这些类在编写程序时可能还不存在。抽象类。
  >>> def factory(aClass,*args):
  ...     return apply(aClass,args)
  ...

  >>>>  ...     def doit(self,message):
  ...             print message
  ...

  >>>>  ...     def __init__(self,name,job):
  ...             self.name=name
  ...             self.job=job
  >>> object1=factory(Spam)
  >>> object2=factory(Person,&quot;diege&quot;,&quot;lily&quot;)
  这里定义了一个对象生成器函数,称为factory.它预期传入的是类对象(任何对象都行),还有该类构造器的一个或多个参数。这个函数使用apply调用该函数并返回实例。
  可以改进之处就是,在构造器调用中支持关键词参数。函数factory能够通过**agrs参数手机参数。当第三个参数传给apply时:
  def factory(aClass,*args,**kagrs):
  ...     return apply(aClass,kargs)
  在Python中一切都是对象。
  五、方法是对象:绑定或无绑定
  方法也是一种对象,很像函数。类方法能有实例或类来读取。实际上Python中就有两种方式。
  无绑定类方法对象:无self
  通过对类进行点号运算从而获取类的函数属性,会传回无绑定(unboud)方法对象。调用该方法时,【必须明确提供实例对象】作为第一个参数。
  绑定实例方法对象:self+函数对
  通过对实例进行全运算从而获取类的函数属性,会传回绑定(bound)方法对象。Python在绑定方法对象中自动把实例和函数打包,所以,不用传递实例去调用该方法。实例已拥有该方法。
  这两种方法都是功能齐全的对象,可四处传递,保持在列表内等。执行时两者都需要第一参数的实例(也就是self的值).
  调用绑定方法对象时,Python会自动提供实例来创建绑定方法对象的实例。也就是说绑定方法对象通常都可以和简单函数对象互换,而且对原本就是针对函数而编写的接口而言,非常用有。

  >>>>  ...     def doit(self,message):
  ...             print message
  >>> object1=Spam()
  >>> object1.doit('hellow world')
  hellow world
  可以帮这个绑定方法赋值给另一个变量名,然后像简单函数那样进行调用。
  >>> object1=Spam()
  >>> x=object1.doit
  >>> x('hello world')
  hello world
  六、类和模块
  都是命名空间
  模块
  * 是数据/逻辑套件
  * 由Python文件或C扩展编写成
  * 通过导入使用
  类
  *实现新的对象
  *由class语句创建
  *通过调用使用
  *总是存在于模块中。
  类支持其他模块不支持的功能。例如,运算符重载,产生多个实例以及继承。
  七、类陷阱
  修改类属性的副作用
  多重继承:顺序很重要
  类,方法以及嵌套作用域
  小结:
  委托:把对象包装在代理类中
  组合:控制嵌入的对象
  继承:从其他类中获取行为
  多重继承,绑定方法,工厂函数

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-546990-1-1.html 上篇帖子: Python 与 ODBC 的陷阱 下篇帖子: [python]-urllib和urllib2模块
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表