Python核心编程读笔 12:OOP
第13章 面向对象编程
一、基本概念
1、object类是所有类的基类,如果你的类没有继承任何其他父类,object 将作为默认的父类。
2、python创建实例时无需new:
myFirstObject = MyNewObjectType() #“函数调用”形式!!!
3、python类的所有非静态方法的第一个形参都是self
4、python创建类时的继承:
class EmplAddrBookEntry(AddrBookEntry): #括弧内的便是基类
……
5、python中所有的类属性均public,但名字可能被“混淆”以阻止未经授权的访问,仅此而已!
6、python中的OOP术语
抽象/实现
封装/接口
合成
派生/继承/继承结构
泛化/特化
多态
自省/反射:
该性质展示了某对象是如何在运行期取得自身信息的。即如果传一个对象给你,你可以查出它有什么能力。
python中的type() dir()等内建函数都使用了反射机制
二、类
1 类
(1)创建类
  class ClassName( bases ): 
  'class documentation string'   #'类文档字符串' 
  class_suite             #类体 
(2)类的属性
属性 = 数据属性 + 方法属性
特殊的类属性:
    C.__name__ 类C的名字(字符串) 
    C.__doc__ 类C的文档字符串 
    C.__bases__ 类C的所有父类构成的元组 
    C.__dict__  类C的属性 
    C.__module__  类C定义所在的模块(1.5 版本新增) 
    C.__class__ 实例C对应的类(仅新式类中)
2 实例
(1)关于__init__()和__del__()方法
不要忘记首先调用父类的__init__()和__del__()
调用del x不表示调用了x.__del__(),其仅是减少x的引用计数,只有当引用计数为1时才会执行__del__()函数
除非你知道你正在干什么,否则不要去实现__del__()
(2)实例属性 和 类属性
内建函数 dir()可以显示类属性,也可以打印所有实例属性
从实例中访问类属性须谨慎:
任何对实例属性的赋值都会创建一个实例属性(如果不存在的话)并且对其赋值。如果类属性中存在同名的属性,则会覆盖对类属性的引用。所以,给一个与类属性同名的实例属性赋值,我们会有效地“隐藏”类属性,但一旦我们删除了这个实例属性,类属性又重见天日。
类属性的持久性
3 绑定与方法调用
方法是类属性而非实例属性;
方法只有在其所属的类拥有实例时,才能被调用。当存在一个实例时,方法才被认为是绑定到那个实例了。没有实例时方法就是未绑定的;
任何一个方法定义中的第一个参数都是变量 self,它表示调用此方法的实例对象
(1)调用绑定方法
即正常的先构建出一个类的实例,然后通过该实例调用该类的方法(因为此时方法已经与实例绑定了!)
(2)调用非绑定方法
调用非绑定方法不常用。调用一个还没有任何实例的类中的方法的主要场景是:你在派生一个子类,而且你要覆盖父类的方法,这时你需要调用那个父类中想要覆盖掉的构造方法:
    class EmplAddrBookEntry(AddrBookEntry): 
    'Employee Address Book Entry class' # 员工地址记录条目 
     def __init__(self, nm, ph, em): 
        AddrBookEntry.__init__( self, nm, ph)  #此即调用非绑定方法。当还没有实例且需要调用一个非绑定方法的时候必须传递self 参数
        self.empid = id 
        self.email = em
(3)静态方法 和 类方法
创建方法1:使用staticmethod()和 classmethod()内建函数
    class TestStaticMethod: 
      def foo(): 
         print 'calling static method foo()' 
      foo = staticmethod(foo)
    class TestClassMethod: 
      def foo(cls): 
         print 'calling class method foo()' 
         print 'foo() is part of class:', cls.__name__ 
      foo = classmethod(foo)
创建方法2:使用函数修饰符
  class TestStaticMethod: 
    @staticmethod 
    def foo(): 
       print 'calling static method foo()' 
  class TestClassMethod: 
    @classmethod 
    def foo(cls): 
       print 'calling class method foo()' 
       print 'foo() is part of class:', cls.__name__
三、组合
在代码中利用类的两种方法:组合 + 继承
组合是一种has-a关系
四、继承、子类和派生
1 __base__类属性
它是一个包含其父类的集合的元组,
2 通过继承覆盖方法
举例说明:
  class P(object): 
     def foo(self): 
        print 'Hi, I am P-foo()'
  class C(P): 
     def foo(self): 
        print 'Hi, I am C-foo()'
  >>> c = C() 
  >>> c.foo() 
  Hi, I am C-foo()  尽管C继承了P的foo()方法,但因为C定义了自已的 foo()方法,所以P中的foo()方法被覆盖
如何调用那个被我覆盖的基类方法呢:
方法一:
  >>> P.foo( c )     这是在调用非绑定方法
  Hi, I am P-foo()
方法二:
  class C(P): 
     def foo(self): 
        P.foo( self )   在子类的重写方法里显式地调用基类方法(也是在调用非绑定方法) 
        print 'Hi, I am C-foo()'
方法三:
  class C(P): 
     def foo(self): 
        super( C, self ).foo() 
        print 'Hi, I am C-foo()'
3 从标准类型派生
举例1:继承不可变标准类型的例子
假定你想在金融应用中,应用一个处理浮点数的子类。每次你得到一个贷币值(浮点数给出的),你都需要通过四舍五入,变为带两位小数位的数值。
  class RoundFloat(float):   继承float
     def __new__(cls, val): 
        return float.__new__(cls, round(val, 2))
或写成:
  class RoundFloat(float): 
     def __new__(cls, val): 
        return super(RoundFloat, cls).__new__(cls, round(val, 2))
举例2:继承可变标准类型的例子
该例子创建一个新的字典类型,其keys()方法会自动排序结果
  class SortedKeyDict(dict): 
     def keys(self): 
        return sorted( super( SortedKeyDict, self ).keys())
4 多重继承
复杂,暂且没看!
五、类、实例和其他对象的内建函数
issubclass(sub, sup)
isinstance(obj, class)
hasattr(myInst, 'foo')
getattr(myInst, 'foo')
setattr(myInst, 'bar', 'my attr')
delattr(myInst, 'foo')
dir( obj )
super( type[, obj] ) 给出type,super()会返回此type的父类。若你希望父类被绑定,你可以传入obj参数(obj可以是type类型的一个实例;obj也可以是一个类型,但应当是type的一个子类)
vars(obj) 返回一个字典,它包含了对象存储于其__dict__中的属性(键)及值
六、用特殊方法定制类
可以重写python中的一些特殊方法以定制类,从而可以实现两大功能:
模拟标准类型
重载操作符
用来定制类的特殊方法列举如下:
基本定制型:
  C.__init__(self[, arg1, ...])              构造器(带一些可选的参数)
  C.__new__(self[, arg1, ...])            构造器(带一些可选的参数);通常用在设置不变数据类型的子类。
  C.__del__(self)                              解构器
  C.__str__(self)                               可打印的字符输出;内建 str()及 print 语句
  C.__repr__(self)                            运行时的字符串输出;内建 repr() 和‘‘ 操作符
  C.__unicode__(self)                      Unicode 字符串输出;内建 unicode()
  C.__call__(self, *args)                   表示可调用的实例
  C.__nonzero__(self)                     为 object 定义 False 值;内建 bool() (从 2.2 版开始)
C.__len__(self) “长度”(可用于类);内建 len()
对象(值)比较:
  C.__cmp__(self, obj)                     对象比较;内建 cmp()
  C.__lt__(self, obj) and                   小于/小于或等于;对应<及<=操作符
  C.__gt__(self, obj) and                  大于/大于或等于;对应>及>=操作符
  C.__eq__(self, obj) and                 等于/不等于;对应==,!=及<>操作符
属性:
  C.__getattr__(self, attr)                 获取属性;内建 getattr();仅当属性没有找到时调用
  C.__setattr__(self, attr, val)          设置属性
  C.__delattr__(self, attr)                 删除属性
  C.__getattribute__(self, attr)         获取属性;内建 getattr();总是被调用
  C.__get__(self, attr)                      (描述符)获取属性
  C.__set__(self, attr, val)                (描述符)设置属性
C.__delete__(self, attr) (描述符)删除属性
数值类型:二进制操作符
  C.__*add__(self, obj)                    加;+操作符
  C.__*sub__(self, obj)                    减;-操作符
  C.__*mul__(self, obj)                    乘;*操作符
  C.__*div__(self, obj)                     除;/操作符
  C.__*truediv__(self, obj)               True 除;/操作符
  C.__*floordiv__(self, obj)              Floor 除;//操作符
  C.__*mod__(self, obj)                  取模/取余;%操作符
  C.__*divmod__(self, obj)             除和取模;内建 divmod()
  C.__*pow__(self, obj[, mod])       乘幂;内建 pow();**操作符
  C.__*lshift__(self, obj)                 左移位;<<操作符
  C.__*rshift__(self, obj)                 右移;>>操作符
  C.__*and__(self, obj)                   按位与;&操作符
  C.__*or__(self, obj)                     按位或;|操作符
C.__*xor__(self, obj) 按位与或;^操作符
数值类型:一元操作符
  C.__neg__(self)                           一元负
  C.__pos__(self)                           一元正
  C.__abs__(self)                           绝对值;内建 abs()
  C.__invert__(self)                        按位求反;~操作符
数值类型:数值转换
  C.__complex__(self, com)            转为 complex(复数);内建 complex()
  C.__int__(self)                           转为 int;内建 int()
  C.__long__(self)                         转为 long;内建 long()
  C.__float__(self)                         转为 float;内建 float()
数值类型:基本表示法(String)
  C.__oct__(self)                           八进制表示;内建 oct()
  C.__hex__(self)                         十六进制表示;内建 hex()
数值类型:数值压缩
  C.__coerce__(self, num)           压缩成同样的数值类型;内建 coerce()
  C.__index__(self)                      在有必要时,压缩可选的数值类型为整型(比如:用于切片索引等等)
序列类型
  C.__len__(self)                         序列中项的数目
  C.__getitem__(self, ind)           得到单个序列元素
  C.__setitem__(self, ind,val)     设置单个序列元素
  C.__delitem__(self, ind)           删除单个序列元素
  C.__getslice__(self, ind1,ind2) 得到序列片断
  C.__setslice__(self, i1, i2,val)   设置序列片断
  C.__delslice__(self, ind1,ind2)  删除序列片断
C.__contains__(self, val) 测试序列成员;内建 in 关键字
  C.__*add__(self,obj)                串连;+操作符
  C.__*mul__(self,obj)                重复;*操作符
C.__iter__(self) 创建迭代类;内建 iter()
映射类型
  C.__len__(self)                         mapping中的项的数目
  C.__hash__(self)                      散列(hash)函数值
  C.__getitem__(self,key)           得到给定键(key)的值
  C.__setitem__(self,key,val)     设置给定键(key)的值
  C.__delitem__(self,key)           删除给定键(key)的值
  C.__missing__(self,key)          给定键如果不存在字典中,则提供一个默认值
1 简单定制举例
目标:自定义一个类来保存浮点数,且自动实现四舍五入并保留两位小数
  class RoundFloatManual(object): 
     def __init__(self, val): 
        assert isinstance(val, float), \ 
        "Value must be a float!" 
        self.value = round( val, 2 )
此时若如下用会出现这样的效果:
  >>> rfm = RoundFloatManual(42) 
  Traceback (most recent call last): 
  File "<stdin>", line 1, in ? 
  File "roundFloat2.py", line 5, in __init__ 
    assert isinstance(val, float), \ AssertionError: Value must be a float! 
  >>> rfm = RoundFloatManual(4.2) 
  >>> rfm             本来此处按常理理解应该打印出浮点数最好,但实际并没有,因为我们没有对类进行定制
  <roundFloat2.RoundFloatManual object at 0x63030> 
  >>> print rfm     本来此处按常理理解应该打印出浮点数最好,但实际并没有,因为我们没有对类进行定制
  <roundFloat2.RoundFloatManual object at 0x63030>
解决办法:好的办法是,去实现__str__()和__repr__()二者之一,或者两者都实现
现添重载__str__()和__repr__()方法,以覆盖默认的行为:
    def __str__(self): 
       return '%.2f' % self.value
__repr__ = __str_ #由于本例中两个函数的代码可以完全一样,所以可以仅让__repr__()作为__str__()的一个别名
这样打印操作就正常了:
  >>> rfm = RoundFloatManual(5.5964) 
  >>> rfm         此处显示正常是由于重写了__repr__()方法的效果
  5.60 
  >>> print rfm    此处显示正常是由于重写了__str__()方法的效果
  5.60
2 数值定制举例
目标:创建一个Time60时间类
class Time60(object):
     def __init__(self, hr, min): # constructor 构造器 
       self.hr = hr       # assign hours 给小时赋值 
       self.min = min  # assign minutes 给分赋值
显示:
  def __str__(self):    #重写方法
     return '%d:%d' % (self.hr, self.min)
__repr__ = __str__ #重写方法
加法:
def __add__(self, other): #重写方法
return self.__class__(self.hr + other.hr, self.min + other.min)
原位加法:用来支持像 mon += tue 这样的操作符
  def __iadd__(self, other):   #重写方法
     self.hr += other.hr 
     self.min += other.min 
     return self
3 迭代器定制举例
class AnyIter(object):
    def __init__(self, data, safe=False): 
      self.safe = safe 
      self.iter = iter(data)
    def __iter__(self): 
      return self
    def next(self, howmany=1): 
      retval = [] 
      for eachItem in range(howmany): 
        try: 
          retval.append( self.iter.next() ) 
        except StopIteration: 
          if self.safe: 
            break 
          else: 
            raise 
      return retval
使用:
   >>> a = AnyIter(range(10)) 
   >>> i = iter(a) 
   >>> for j in range(1,5): 
   >>> ... print j, ':', i.next(j) 
   1 : [0] 
   2 : [1, 2] 
   3 : [3, 4, 5] 
   4 : [6, 7, 8, 9]
七、私有化
python的属性默认是public
双下划线:
由双下划线开始的属性在运行时被“混淆”,所以不允许直接访问
单下划线:(验证有问题?)
简单的模块级私有化只需要在属性名前使用一个单下划线字符。这就防止模块的属性用“from mymodule import*”来加载。这是严格基于作用域的,所以这同样适合于函数。
八、授权与包装
九、新式类的高级特性
 
                     
                    
                 
                    
                


 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号