• 幕客老师召集小伙伴
  • 运维高手36项修炼
  • python自动化运维项目实战
  • nginx从入门到实战
  • 阿里云与Centos7实战

Python课程四、高级进阶

3.4 面向对象编程

3.4.1 面向对象编程思想

一、类与实例

    类:就是多有对象抽象出来的一个定义

    实例:是真正的实物,提现类中具体的信息

    2 、Python能好的支持面向对象编程,但是不强制写oo方面的程序。

二、实例化过程

 

 


 

3.4.2 类创建和类属性

 

class MyNewObjectType(bases):

    'define MyNewObjectType class'

    class_suite #类体 

bases是所要继承的父类,可以是一个或者多个。

object是所有类的父类,默认父类。

类名通常由大写字母打头 

一、实例:

  class MyData(object):
    pass
    >>> mathObj = MyData()
    >>> mathObj.x = 4     #是实例的属性,并非类的属性。
    >>> mathObj.y = 5
    >>> mathObj.x + mathObj.y
    9
    >>> mathObj.x * mathObj.y
    20

二、 方法:

class MyDataWithMethod(object): # 定义类

    def printFoo(self): # 定义方法
    print 'You invoked printFoo()!' 

>>> myObj = MyDataWithMethod() # 创建实例

>>> myObj.printFoo() # 现在调用方法
You invoked printFoo()! 

1、注意到了 self 参数,它在所有的方法声明中都存在。这个参数代表实例对象本身,当你 用实例调用方法时,由解释器悄悄地传递给方法的,所以,你不需要自己传递 self 进来,因为它是 自动传入的

2、Python 创建实例后,在实例化过程中,调用__init__()方法,当一个类被实例化时,就 可以定义额外的行为。

class AddrBookEntry(object): # 类定义 'address book entry class'

     def __init__(self, nm, ph): # 定义构造器,当updatePhone()被调用时,隐喻的先调用它。

        self.name = nm # 设置 name  
        self.phone = ph # 设置 phone
        print 'Created instance for:', self.name

    def updatePhone(self, newph): # 定义方法

        self.phone = newph
        print 'Updated phone    # for:', self.name 

 

三、创建一个实例(实例化)

>>> john = AddrBookEntry('John Doe', '408-555-1212') #为 John Doe 创建实例

>>> jane = AddrBookEntry('Jane Doe', '650-555-1212') #为 Jane Doe 创建实例 

 

四、访问实例属性

>>> john
    <__main__.AddrBookEntry instance at 80ee610>
    >>> john.name
     'John Doe'
     >>> john.phone
     '408-555-1212'
     >>> jane.name
    'Jane Doe'
    >>> jane.phone
    '650-555-1212'

五、方法调用

>>> john.updatePhone('415-555-1212') #更新 John Doe 的电话

>>> john.phone
'415-555-1212' 

 

六、创建子类

 

class EmplAddrBookEntry(AddrBookEntry): #继承父类

          'Employee Address Book Entry class'#员工地址本类

        def __init__(self, nm, ph, id, em):  #子类重写构造器,避免调用父类
            AddrBookEntry.__init__(self, nm, ph)
            self.empid = id
            self.email = em
        def updateEmail(self, newem):
            self.email = newem
            print 'Updated e-mail address for:', self.name

EmplAddrBookEntry这个子类继承了父类的 updatePhone() 方法

>>> john = EmplAddrBookEntry('John Doe', '408-555-1212',42, 'john@spam.doe')

Created instance for: John Doe #给 John Doe 创建实例

 

>>> john.updateEmail('john@doe.spam')  #Updated e-mail address for: John Doe

 


 

3.4.3 实例及实例属性

 

In [1]: class C(object):

   ...:     foo = 100    

   ...: 

In [2]: print C.foo

100

In [4]: C.foo += 1

In [5]: C.foo    

Out[5]: 101

#从实例修改类属性

In [3]: c.foo

Out[3]: 100

In [4]: c.foo = 200

In [5]: c.foo

Out[5]: 200

In [6]: C.foo #类属性没有被实例属性,所修改

Out[6]: 100

In [7]: del c.foo  #删除实例属性,访问正常

In [8]: C.foo

Out[8]: 100

In [9]: c.foo

Out[9]: 100

#在类属性可变的情况下,通过实例修改类属性,一切都不同了 !

In [10]: class C(object):

   ....:     x = {"name":"Tom"}

   ....: 

In [11]: c=C()

In [12]: c.x

Out[12]: {'name': 'Tom'}

In [13]: c.x["name"]="NewTom"

In [14]: C.x

Out[14]: {'name': 'NewTom'}

In [15]: del c.x

AttributeError: 'C' object attribute 'x' is read-only

#提示:从实例访问类属性需谨慎

In [16]: del C.x

 


 

3.4.4 经典类和新式类区别

python2.1之前用的经典类,在python2.2版本后加入了新式类,

最大的区别:定义书写时新式类,有类继承。

区别:在多重继承时,查找父类的方式不一样。

开发中建议使用新式类。

 

一、新式类


每个类都继承于一个基类,可以是自定义类或者其它类,如果什么都不想继承,那就继承于object
如果想用super调用父类的构造函数,请使用新式类!

#基类,新式类              

class Person(object):  

    def __init__(self):

        print "Hi, I am a person."

#子类                       

class Student(Person): 

    def __init__(self):

        super(self.__class__, self).__init__() #super()调用父类的同名属性

                       

if __name__ == "__main__":

    student = Student()

 

二、经典类


注意:如果经典类被作为父类,子类调用父类的构造函数时会出错。

#经典类

class Person:                              

    def __init__(self):                    

        print "Hi, I am a person. "        

#子类     

class Student(Person):                     

    def __init__(self):                    

        super(self.__class__, self).__init__()      

     

if __name__ == "__main__":                 

    student = Student()

#error:TypeError: must be type, not classobj

 

注意:在python3.0后已经可以把经典类和新式类作了统一,没有存在super()调用的问题。

 

三、经典类和新式类的区别

#经典类 深度优先

In [5]: class A:

   ...:     attr = 1

   ...: 

In [6]: class B(A):

   ...:     pass

   ...:  

In [7]: class C(A):

   ...:     attr = 2

   ...:  

In [8]: class D(B,C):

   ...:     pass

   ...: 

In [9]: x = D()

In [10]: x.attr

Out[10]: 1

 #新式类,广度优先

In [11]: class A(object):attr = 1 

In [12]: class B(A):pass

In [13]: class C(A):attr = 2

In [14]: class D(B,C):pass

In [15]: x = D()

In [16]: x.attr

Out[16]: 2

#Python2.0 和 Python 3.0后经典类和新式类统一

In [17]: class A(): #用python3.0来运行,会出现__new__ 方法。

   ....:     pass

   ....: 

In [18]: print (dir(A))

['__doc__', '__module__']

 

三、__bases__类属性 

返回父类的类型元组

 

In [78]: class A(object): pass  

In [79]: class B(object): pass        

In [80]: class C(A,B):pass 

In [81]: C.__bases__

Out[81]: (__main__.A, __main__.B)

 


 

3.4.5 子类和派生类

一、子类派生类

    class SubClassName (ParentClass1[, ParentClass2, ...]):
     'optional class documentation string'
     class_suite


class Parent(object): # 定义父类

     def parentMethod(self):
         print 'calling parent method'

class Child(Parent): # 定义子类

      def childMethod(self):

          print 'calling child method'
>>> p = Parent() #父类的实例

>>>p.parentMethod()

>>>

>>> c = Child()# instance of child 子类的实例

>>> c.childMethod() # child calls its method calling child method
>>> c.parentMethod() # calls parent's method calling parent method 

 

二、父类方法重写

重写__init__不会自动调用基类的__init__ 

class P(object):
     def __init__(self):

           print "calling P's constructor"

class C(P):
     def __init__(self):

       print "calling C's constructor"

>>> c = C()
calling C's constructor

#没有重写,将从父类中得到继承

In [19]: class P(object):

   ....:      def __init__(self):

   ....:            print "calling P's constructor"

   ....:  

In [20]: class C(P):

   ....:     pass

   ....: 

In [21]: c=P()

calling P's constructor

 


 

3.4.6 内建函数

 一、issubclass()

布尔函数判断一个类是另一个类(或父类的元组)的子类或子孙类  True/False

二、isinstance()

判定一个对象是否是另一个给定类的实例 

isinstance(obj1, obj2)

In [86]: isinstance(c1, C1)

Out[86]: True

In [87]: isinstance(c2, C1)

Out[87]: False

In [88]: isinstance(4, int)

Out[88]: True

In [89]: isinstance(4, str)

Out[89]: False

 

三、其它内建函数

内建函数                                 描述

hasattr(obj, attr)                    如果 obj 有属性 attr(用字符串给出),返回 True,反之,返回False

getattr(obj, attr[, default])      获取 obj 的 attr 属性;与返回 obj.attr 类似;如果 attr不是 obj 的属性,如果提供了默认值,则返回默认值;不然, 就会引发一个 AttributeError 异常。 

setattr(obj, attr, val)               设置obj的attr属性值为val,替换任何已存在的属性值; 不然,就创建属性;类似于 obj.attr=val 

delattr(obj, attr)                   从 obj 中删除属性 attr(以字符串给出);类似于 del obj.attr。 
vars(obj=None)                    返回 obj 的属性及其值的一个字典;如果没有给出 obj, vars()显示局部名字空间字典(属性及其值),也就是 locals()。 

 

  >>> class myClass(object):

          ... def __init__(self):

               ... self.foo = 100 ...

>>> myInst = myClass()
>>> hasattr(myInst, 'foo')
True
>>> getattr(myInst, 'foo')
100
>>> hasattr(myInst, 'bar') False
>>> getattr(myInst, 'bar') Traceback (most recent call last):
File "<stdin>", line 1, in ?
getattr(myInst, 'bar')
AttributeError: myClass instance has no attribute 'bar'
>>> getattr(c, 'bar', 'oops!')
'oops!'

  >>> setattr(myInst, 'bar', 'my attr')
  >>> dir(myInst)
['__doc__', '__module__', 'bar', 'foo']
>>> getattr(myInst, 'bar') # same as myInst.bar

'my attr'
>>> delattr(myInst, 'foo')
>>> dir(myInst)
['__doc__', '__module__', 'bar']
>>> hasattr(myInst, 'foo')
False
  class C(object):
     pass
 >>> c = C()
 >>> c.foo = 100
 >>> c.bar = 'Python'
 >>> c.__dict__
    {'foo': 100, 'bar': 'Python'}
 >>> vars(c)
     {'foo': 100, 'bar': 'Python'}



 

3.4.7 类补充

一、类的组合

#当较小的类是较大类的组件时,比较合适利用

class NewAddrBookEntry(object): # class definition 类定义 'new address book entry class'
    def __init__(self, nm, ph): # define constructor 定义构造器

    self.name = Name(nm) # create Name instance 创建 Name 实例

    self.phone = Phone(ph) # create Phone instance 创建 Phone 实例

    print 'Created instance for:', self.name 

NewAddrBookEntry这个类由多个类Name、Phone组合而成。

 

二、继承

1、在子类不重写的情况下,子类会继承父类的方法和属性(__doc__除外)。

2、如果子类重写了父类的方法,又要调用父类的方法,可以通过如下方式进行。

  1. class A:  
  2.     def method(self, arg):    
  3.         pass  
  4.   
  5. class B(A):  
  6.     def method(self, arg):  
  7. #        A.method(self,arg)                # 1  
  8. #        super(B, self).method(arg)    #2  
  9.         super().method(arg)    

例子:

In [22]: class P(object):

   ....:     def foo(self):

   ....:         print 'Hi I am P-foo()'

   ....: 

 

In [23]: p = P()

 

In [24]: p.foo()

Hi I am P-foo()

#重写基类方法

In [25]:  class C(P):

   ....:     def foo(self):

   ....:         super(C, self).foo()

   ....:         print 'Hi, I am C-foo()'

   ....:  

In [26]: c=C()

In [27]: c.foo()

Hi I am P-foo()

Hi, I am C-foo()

#super的亮点在于,不不需要自己去指明基类的方法,自己回去寻找识别。

super(c.__class__,c).foo() #通过实例执行父类的方法

 

三、标准类型子类化

经典类不能子类话化准类型,新式类可以。

__new__是新式类中出现的方法,作用于构造方法__init__之前。

__new__()决定是否 要使用该__init__()方法,因为__new__()可以调用其他类的构造方法或者直接返回别的对象来作为本类 的实例。

 

1、子类化不可变类型

  改写float对对象小数点后进行四舍五入

class RoundFloat(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)) #改用super

 

2、子类化可变类型

#返回字典键,直接重新改写,父类方法keys,同时对返回 key进行排序

class SortedKeyDict(dict):
     def keys(self):
         return sorted(super( SortedKeyDict, self).keys())

or

   class SortedKeyDict(dict):

      def keys(self):
          return sorted(self.keys())

 

 In [29]: RoundFloat(1.223) + RoundFloat(1.224) 

Out[29]: 2.44

 


 

3.4.8 类的制定

一、用特殊方法制定类     

1、作用

模拟标准类型

重载操作符 

2、python的类特殊方法

 

二、简单定制

定制float函数,精确到小数点后两位。

In [31]: class RoundFloatManual(object):

   ....:         def __init__(self, val):

   ....:             assert isinstance(val, float), "Value must be a float!"

   ....:             self.value = round(val, 2)

   ....:

In [32]: RoundFloatManual(42)   #输入一个不符合要求的类型

AssertionError: Value must be a float! 

 

In [34]: RoundFloatManual(0.42) #输入正确的类型,显示不出想要显示的结果。在构造器也无法显示。

Out[34]: <__main__.RoundFloatManual at 0x106cfc0d0>

In [35]: print RoundFloatManual(0.42)

<__main__.RoundFloatManual object at 0x106cfc250>

 

#因为print(使用 str()) 也不能显示,所以,一个好的办法是,去实现__str__()和__repr__()二者之一 

In [41]: class RoundFloatManual(object):

   ....:         def __init__(self, val):

   ....:             assert isinstance(val, float), \

   ....:             "Value must be a float!"

   ....:             self.value = round(val, 2)

   ....:         def __str__(self):

   ....:             return str(self.value)

   ....: 

 

In [42]: RoundFloatManual(5.590464)

Out[42]: <__main__.RoundFloatManual at 0x106df9b50>

 

In [43]: print RoundFloatManual(5.590464)  #可以print输出,但是终端还是显示的对象的符号。

5.59

#这个时候,我们可以设置__repr__ = __str__来做

 

In [44]: class RoundFloatManual(object):

   ....:         def __init__(self, val):

   ....:             assert isinstance(val, float), \

   ....:             "Value must be a float!"

   ....:             self.value = round(val, 2)

   ....:         def __str__(self):

   ....:             return str(self.value)

   ....:         def __repr__(self):

   ....:             return str(self.value)

   ....: 

 

In [45]: RoundFloatManual(12.2222)

Out[45]: 12.22

In [50]: RoundFloatManual(5.5960)

   ....: 

Out[50]: 5.6  #如果我们想显示出带两位小数点的浮点数

#显示带小数点后两位的数字

In [51]: class RoundFloatManual(object):

   ....:         def __init__(self, val):

   ....:             assert isinstance(val, float), \

   ....:             "Value must be a float!"

   ....:             self.value = round(val, 2)

   ....:         def __str__(self):

   ....:             return '%.2f' % self.value #输出格式化

   ....:         __repr__ = __str__

   ....: 

 

In [52]: RoundFloatManual(5.5960)

Out[52]: 5.60

 

In [27]: RoundFloatManual(1.223) + RoundFloatManual(1.224)

Out[27]: 2.447

 

三、数值定值

模拟一个时间计数器,计算如:某服务运行时间,在线等待时间等等。

 

#!/usr/bin/env python

class Time60(object):
'Time60 - track hours and minutes'

def __init__(self, hr, min):      #定义构造器
'Time60 constructor - takes hours and minutes'
self.hr = hr
self.min = min

def __str__(self): #格式化输出 如:11:22
'Time60 - string representation'
return '%d:%d' % (self.hr, self.min)

__repr__ = __str__   #控制输出

def __add__(self, other):
'Time60 - overloading the addition operator'
return self.__class__(self.hr + other.hr,
self.min + other.min)            #计算时间间隔用普通的加法

def __iadd__(self, other):       #计算时间间隔,用原位加法,即不生产新的对象。
'Time60 - overloading in-place addition'
self.hr += other.hr
self.min += other.min
return self

#原位校验方式

    >>> mon =Time60(10,30)
    >>> tue =Time60(11,15)
    >>> mon
    10:30
    >>> id(mon)
    401872
    >>> mon += tue
    >>> id(mon)
    401872

>>> mon 21:45 

四、迭代器

一、实现一个无穷迭代 

#!/usr/bin/env python

from random import choice 

class RandSeq(object):
    def __init__(self, seq):   #_init__()方法执行前述的赋值操作。
    self.data = seq

def __iter__(self):    #__iter__()仅返回 self,这就是如何将一个对象声明为 迭代器的方式 。

    return self

def next(self):    #next()来得到迭代器中连续的值 


    return choice(self.data)

 

#执行

In [4]:from randseq import RandSeq

 

In [5]: for eachItem in RandSeq(('rock', 'paper', 'scissors')):        

                   print eachItem

二、实现任意项的迭代器 

创建一个迭代对象,

传给 next()方法一个参数,控制返回 条目的数目。 

#!/usr/bin/env python

class AnyIter(object):
    def __init__(self, data, safe=0):
        self.safe = safe
        self.iter = iter(data)

   def __iter__(self):
        return self

   def next(self, howmany=1):   #howmany控制一次返回多少个数
        retval = []
        for eachItem in range(howmany):
            try:
                retval.append(self.iter.next())  #调用迭代器获取迭代的每一个,到列表中
            except StopIteration:
               if self.safe:          #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]

#选择不安全的模式

>>> i = iter(a)
>>> i.next(14)
    Traceback (most recent call last):
    File "<stdin>", line 1, in ?
    File "anyIter.py", line 15, in next retval.append(self.iter.next())
    StopIteration

#选择安全的模式

>>> a = AnyIter(range(10), True)
>>> i = iter(a)
>>> i.next(14)
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

三、多类型定制

四、

3.4.9 类包装

3.4.10 新式类的高级特性

3.4.11 类方法、静态方法操作

https://www.zhihu.com/question/20021164

 

Python课程四、高级进阶

Pingbacks已打开。

引用地址

暂无评论

发表评论