python - 面向对象编程(初级篇)

写了这么多python 代码,也常用的类和对象,这里准备系统的对python的面向对象编程做以下介绍。

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)

面向对象:对函数进行分类和封装,让开发“更快更好更强...”

 

创建类和对象

面向对象编程是一种编程方式,此编程方式的落地需要使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” 的使用。

  类就是一个模板,模板里可以包含多个函数,函数里实现一些功能

  对象则是根据模板创建的实例,通过实例对象可以执行类中的函数

创建一个类

class EmployeeInfo():
    '创建一个员工信息类'   # 类文档字符串 位置在类名下单引号中, 类的帮助信息可以通过ClassName.__doc__查看。

    #class_suite   类体  由类成员,方法,数据属性组成。

print EmployeeInfo.__doc__   # 创建一个员工信息类

 

应用实例

# 创建一个类,应用实例

class EmployeeInfo():
    '员工信息类'

    # 定义员工数量,初始化为0 ,每实例化一个对象时,数量增 1
    empCount = 0

    def __init__(self,name,age):  # 类中的函数第一个参数必须是self(详细见:类的三大特性之封装)类中定义的函数叫做 “方法”

        self.name = name
        self.age = age
        EmployeeInfo.empCount += 1

    def displayEmployeeInfo(self):

        print "员工姓名:",self.name,"员工年龄:",self.age,"员工序号:",self.empCount


emp1 = EmployeeInfo("Tester",18)

emp1.displayEmployeeInfo()  # 输出结果 员工姓名: Tester 员工年龄: 18 员工序号: 1

emp2 = EmployeeInfo("Manager",19) 

emp2.displayEmployeeInfo()  # 员工姓名: Manager 员工年龄: 19 员工序号: 2

 

 

  • empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。
  • 第一种方法__init__()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法(可以不写,根据实际需要自己定义需要的构造函数)。

  • self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。

注意:self 代表的是一个实例,如果使用self.变量,标识的是实例化的变量,如果要使用类变量,必须使用,类名.变量。如:EmployeeInfo.empCount += 1。

self代表类的实例,而非类

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。

# 创建一个测试类

class Test:
    '创建一个测试类'
    def prt(self):
        print(self)
        print(self.__class__)
t = Test()
t.prt()

以上实例执行结果为:

<__main__.Test instance at 0x00000000026CB088>
__main__.Test

从执行结果可以很明显的看出,self 代表的是类的实例,代表当前实例对象的地址,而self.__class__ 则指向该类。

 

类的属性

你也可以使用以下函数的方式来访问属性:

  • getattr(obj, name[, default]) : 访问对象的属性。
  • hasattr(obj,name) : 检查是否存在一个属性。
  • setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
  • delattr(obj, name) : 删除属性。

应用举例

# 如果存在 'age' 属性返回 True。
print hasattr(emp1,'age') # True
# 返回 'age' 属性的值
print getattr(emp1,'age') # 18
# 添加属性 'num' 值为 1
print setattr(emp1,'num',1) # None
# 返回 'num' 属性的值
print getattr(emp1,'num') # 1
# 删除属性 'age'
print delattr(emp1,'num') # None

print getattr(emp1,'num') # 返回为空

 

Python内置类属性

  • __dict__ : 类的属性(包含一个字典,由类的数据属性组成)
  • __doc__ :类的文档字符串
  • __name__: 类名
  • __module__: 类定义所在的模块(类的全名是'__main__.className',如果类位于一个导入模块mymod中,那么className.__module__ 等于 mymod)
  • __bases__ : 类的所有父类构成元素(包含了一个由所有父类组成的元组)
# 示例
print "EmployeeInfo.__doc__:", EmployeeInfo.__doc__
print "EmployeeInfo.__name__:", EmployeeInfo.__name__
print "EmployeeInfo.__module__:", EmployeeInfo.__module__
print "EmployeeInfo.__bases__:", EmployeeInfo.__bases__
print "EmployeeInfo.__dict__:", EmployeeInfo.__dict__

执行结果

EmployeeInfo.__doc__: 员工信息类
EmployeeInfo.__name__: EmployeeInfo
EmployeeInfo.__module__: __main__
EmployeeInfo.__bases__: ()
EmployeeInfo.__dict__: {'__module__': '__main__', 'empCount': 2, '__doc__': '\xe5\x91\x98\xe5\xb7\xa5\xe4\xbf\xa1\xe6\x81\xaf\xe7\xb1\xbb', '__init__': <function __init__ at 0x00000000026F2E48>, 'displayEmployeeInfo': <function displayEmployeeInfo at 0x00000000026F2EB8>}

 

python 析构函数 

析构函数 __del__ ,__del__在对象销毁的时候被调用当对象不再被使用时,__del__方法运行:

class Point:
   def __init__( self, x=0, y=0):
      self.x = x
      self.y = y
   def __del__(self):
      class_name = self.__class__.__name__
      print class_name, "销毁"
 
pt1 = Point()
pt2 = pt1
pt3 = pt1
print id(pt1), id(pt2), id(pt3) # 打印对象的id
del pt1
del pt2
del pt3

以上实例运行结果如下:

3083401324 3083401324 3083401324
Point 销毁

注意:通常你需要在单独的文件中定义一个类,当然python中一个文件也可以定义多个类,不推荐。

类的继承

继承,面向对象中的继承和现实生活中的继承相同,即:子可以继承父的内容。

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。

需要注意的地方:继承语法 class 派生类名(基类名)://... 基类名写在括号里,基本类是在类定义的时候,在元组之中指明的。

在python中继承中的一些特点:

  • 1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。
  • 2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。区别于在类中调用普通函数时并不需要带上self参数
  • 3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

如果在继承元组中列了一个以上的类,那么它就被称作"多重继承" 。

语法:

派生类的声明,与他们的父类类似,继承的基类列表跟在类名之后,如下所示:

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

 

示例

class Parent:        # 定义父类
   parentAttr = 100
   def __init__(self):
      print "调用父类构造函数"
 
   def parentMethod(self):
      print '调用父类方法'
 
   def setAttr(self, attr):
      Parent.parentAttr = attr
 
   def getAttr(self):
      print "父类属性 :", Parent.parentAttr
 
class Child(Parent): # 定义子类
   def __init__(self):
      print "调用子类构造方法"
 
   def childMethod(self):
      print '调用子类方法'
 
c = Child()          # 实例化子类
c.childMethod()      # 调用子类的方法
c.parentMethod()     # 调用父类方法
c.setAttr(200)       # 再次调用父类的方法 - 设置属性值
c.getAttr()          # 再次调用父类的方法 - 获取属性值

以上代码执行结果如下:

调用子类构造方法
调用子类方法
调用父类方法
父类属性 : 200

 

你可以继承多个类

class A:        # 定义类 A
.....

class B:         # 定义类 B
.....

class C(A, B):   # 继承类 A 和 B
.....

 

那么问题又来了,多继承呢?

  • 是否可以继承多个类
  • 如果继承的多个类每个类中都定了相同的函数,那么那一个会被使用呢?

1、Python的类可以继承多个类,Java和C#中则只能继承一个类

2、Python的类如果继承了多个类,那么其寻找方法的方式有两种,分别是:深度优先广度优先

 

  • 当类是经典类时,多继承情况下,会按照深度优先方式查找
  • 当类是新式类时,多继承情况下,会按照广度优先方式查找

经典类和新式类,从字面上可以看出一个老一个新,新的必然包含了跟多的功能,也是之后推荐的写法,从写法上区分的话,如果 当前类或者父类继承了object类,那么该类便是新式类,否则便是经典类。

 

class D:

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> D --> C
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

经典类多继承

 

class D(object):

    def bar(self):
        print 'D.bar'


class C(D):

    def bar(self):
        print 'C.bar'


class B(D):

    def bar(self):
        print 'B.bar'


class A(B, C):

    def bar(self):
        print 'A.bar'

a = A()
# 执行bar方法时
# 首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错
# 所以,查找顺序:A --> B --> C --> D
# 在上述查找bar方法的过程中,一旦找到,则寻找过程立即中断,便不会再继续找了
a.bar()

新式类多继承

 

经典类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去D类中找,如果D类中么有,则继续去C类中找,如果还是未找到,则报错

新式类:首先去A类中查找,如果A类中没有,则继续去B类中找,如果B类中么有,则继续去C类中找,如果C类中么有,则继续去D类中找,如果还是未找到,则报错

注意:在上述查找过程中,一旦找到,则寻找过程立即中断,便不会再继续找了

 

你可以使用issubclass()或者isinstance()方法来检测。

  • issubclass() - 布尔函数判断一个类是另一个类的子类或者子孙类,语法:issubclass(sub,sup)
  • isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

示例

class Parent:  # 定义父类
    parentAttr = 100

    def __init__(self):
        print "调用父类构造函数"

    def parentMethod(self):
        print '调用父类方法'

    def setAttr(self, attr):
        Parent.parentAttr = attr

    def getAttr(self):
        print "父类属性 :", Parent.parentAttr


class Child(Parent):  # 定义子类
    def __init__(self):
        print "调用子类构造方法"

    def childMethod(self):
        print '调用子类方法'

p = Parent

c = Child()          # 实例化子类
c.childMethod()      # 调用子类的方法
c.parentMethod()     # 调用父类方法
c.setAttr(200)       # 再次调用父类的方法 - 设置属性值
c.getAttr()          # 再次调用父类的方法 - 获取属性值

print issubclass(Child,Parent) #  True  issubclass() - 布尔函数判断第一个类是第二个类的子类或者子孙类,语法:issubclass(子类名,父类名)

print isinstance(c,Parent) # True  isinstance(obj, Class) 布尔函数如果obj是Class类的实例对象或者是一个Class子类的实例对象则返回true。

 

方法重写

 如果你的父类方法的功能不能满足你的需求,你可以在子类重写你父类的方法:

 示例

class Parent:        # 定义父类
   def myMethod(self):
      print '调用父类方法'
 
class Child(Parent): # 定义子类
   def myMethod(self):
      print '调用子类方法'
 
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法

执行以上代码输出结果如下:

 

调用子类方法

 

 多态 

 多态(Polymorphism):意味着可以对不同类的对象使用同样的操作,他们会像被“施了魔法一般”工作。

 

 

class F1:
    pass


class S1(F1):

    def show(self):
        print 'S1.show'


class S2(F1):

    def show(self):
        print 'S2.show'

def Func(obj):
    print obj.show()

s1_obj = S1()
Func(s1_obj)   # 'S1.show'
s2_obj = S2() 
Func(s2_obj)
# 'S2.show'
Python “鸭子类型”

 

 

class A:    
    def prt(self):    
        print "A"    
    
class B(A):    
    def prt(self):    
        print "B"    
            
class C(A):    
    def prt(self):    
        print "C"    
           
class D(A):    
    pass    
    
class E:    
    def prt(self):    
        print "E"    
    
class F:    
    pass    
    
def test(arg):    
    arg.prt()    
    
a = A()    
b = B()    
c = C()    
d = D()    
e = E()    
f = F()    
    
test(a)    
test(b)    
test(c)    
test(d)    
test(e)    
test(f)    

 

输出结果: 

 

A  
B  
C  
A  
E  
Traceback (most recent call last):  
  File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 33, in <module>  
    test(a),test(b),test(c),test(d),test(e),test(f)  
  File "/Users/shikefu678/Documents/Aptana Studio 3 Workspace/demo/demo.py", line 24, in test  
    arg.prt()  
AttributeError: F instance has no attribute 'prt'  

a,b,c,d都是A类型的变量,所以可以得到预期的效果(从java角度的预期),e并不是A类型的变量但是根据鸭子类型,走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子,e有prt方法,所以在test方法中e就是一个A类型的变量,f没有prt方法,所以f不是A类型的变量。

 

posted @ 2017-10-19 23:36  Blue·Sky  阅读(777)  评论(0编辑  收藏  举报