内容|上一节(5.1再说字典)|下一节(6生成器)5.2类和封装创建类时,通常会尝试封装类的内部细节。本节介绍Python编程中与封装(包括私有变量和私有属性)相关的习语。PublicvsPrivate尽管类的主要作用之一是封装对象的属性和内部实现细节。但是,该类还定义了一个公共接口(publicinterface),供外界用来操作该对象。实现细节和公共接口之间的区别很重要。问题在Python中,几乎所有与类和对象相关的东西都是公开的。可以轻松查看对象的内部细节。可以随意修改。没有访问控制的概念(例如:私有类成员)。如何隔离内部实现的细节是个问题。Python封装Python依靠编程约定来指示某物的用途。这是约定基于命名的地方。普遍的态度是程序员应该遵守规则而不是让语言强制执行。私有属性任何以下划线_开头的属性都被认为是私有的。classPerson(object):def__init__(self,name):self._name=0如前所述,这是一种编程风格。您仍然可以访问和修改这些私有属性。>>>p=Person('Guido')>>>p._name'Guido'>>>p._name='Dave'>>>一般来说,以下划线_开头的名字被认为是内部实现,无论是该名称是变量名、函数名或模块名。如果您发现自己直接使用这些名称,则您可能做错了什么。您应该寻找更高级的功能。简单属性考虑这个类:classStock:def__init__(self,name,shares,price):self.name=nameself.shares=sharesself.price=price这是一个惊人的特性,你可以给属性设置任何值:>>>s=Stock('IBM',50,91.1)>>>s.shares=100>>>s.shares="hundred">>>s.shares=[1,0,0]>>>你可能要检查这个,然后应该触发TypeError异常):s.shares='50'#引发TypeError,这是一个字符串此时你会做什么?托管属性方法一:引入访问器方法(accessormethods)。classStock:def__init__(self,name,shares,price):self.name=nameself.set_shares(shares)self.price=price#分层“get”操作的函数defget_shares(self):returnself._shares#分层“set”操作的函数defset_shares(self,value):ifnotisinstance(value,int):raiseTypeError('Expectedanint')self._shares=value不幸的是,这破坏了我们现有的代码。例如:之前通过s.shares=50给shares赋值,现在要改成s.set_shares(50)给shares赋值,这是很不好的。属性方法二:classStock:def__init__(self,name,shares,price):self.name=nameself.shares=sharesself.price=price@propertydefshares(self):returnself._shares@shares.setterdefshares(self,value):ifnotisinstance(value,int):raiseTypeError('Expectedint')self._shares=value现在,正常的属性访问会触发shares.setter下的@property和@Getter和setter方法。>>>s=Stock('IBM',50,91.1)>>>s.shares#Triggers@property50>>>s.shares=75#Triggers@shares.setter>>>使用该方法,无需更新source对代码进行任何更改。当类内部有赋值时(包括__init__()方法内部),直接调用新的setter:classStock:def__init__(self,name,shares,price):...#这个赋值调用self下面的setter.shares=shares......@shares.setterdefshares(self,value):ifnotisinstance(value,int):raiseTypeError('Expectedint')self._shares=valuetraitattributeandprivatename(私有名称的使用经常会引起混淆)。虽然特征属性在内部使用了私有名称,例如_shares。在类的其他地方(不是attribute属性),您仍然可以继续使用shares等名称。要素属性对于计算数据属性也非常有用。类Stock:def__init__(self,name,shares,price):self.name=nameself.shares=sharesself.price=price@propertydefcost(self):returnself.shares*self.price...this允许您删除cost之后的括号,隐藏cost是一个方法的事实:>>>s=Stock('GOOG',100,490.1)>>>s.shares#Instancevariable100>>>s.cost#Computed值49010。0>>>统一访问最后一个例子展示了如何在一个对象上放置一个更统一的接口。如果不这样做,这些对象可能难以使用。>>>s=Stock('GOOG',100,490.1)>>>a=s.cost()#Method49010.0>>>b=s.shares#数据属性100>>>为什么要加括号在成本()之后,但不需要份额?特征属性可以解决这个问题。装饰器语法@语法称为“装饰”。它指定一个修饰符应用于紧随其后的函数定义:...@propertydefcost(self):returnself.shares*self.price更多细节在第7节中给出。插槽属性(__slots__)您可以使用__slots__限制属性名称集:classStock:__slots__=('name','_shares','price')def__init__(self,name,shares,price):self.name=name...会触发错误使用其他属性时:>>>s.price=385.15>>>s.prices=410.2Traceback(最近一次调用):文件“
