python009 -- 类

什么是面向对象编程?

面向对象编程(Object Oriented Programming,OOP,面向对象程序设计)是一种计算机编程架构。Python就是这种编程语言。

面向对象程序设计中的概念主要包括:对象、类、继承、动态绑定、封装、多态性、消息传递、方法。

1)对象:类的实体,比如一个人。

2)类:一个共享相同结构和行为的对象的集合。通俗的讲就是分类,比如人是一类,动物是一类。

3)继承:类之间的关系,比如猫狗是一类,他们都有四条腿,狗继承了这个四条腿,拥有了这个属性。

4)动态绑定:在不修改源码情况下,动态绑定方法来给实例增加功能。

5)封装:把相同功能的类方法、属性封装到类中,比如人两条腿走路,狗有四条腿走路,两个不能封装到一个类中。

6)多态性:一个功能可以表示不同类的对象,任何对象可以有不同的方式操作。比如一个狗会走路、会跑。

7)消息传递:一个对象调用了另一个对象的方法。

8)方法:类里面的函数,也称为成员函数。

对象=属性+方法。

属性:变量。

方法:函数。

实例化:创建一个类的具体实例对象。比如一条泰迪。

什么是类?

类是对对象的抽象,对象是类的实体,是一种数据类型。它不存在内存中,不能被直接操作,只有被实例化对象时,才会变的可操作。

类是对现实生活中一类具有共同特征的事物的抽象描述。

6.1 类和类方法语法

1
2
3
4
5
6
# 类
class  ClassName():
    pass
     # 类中的方法
     def  funcName( self ):
       pass

self代表类本身。类中的所有的函数的第一个参数必须是self。

6.2 类定义与调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  MyClass():
     =  100
     def  func( self , name):
         return  "Hello %s!"  %  name
     def  func2( self ):
         return  self .x
mc  =  MyClass()   # 类实例化,绑定到变量mc
print  mc.x    # 类属性引用
print  mc.func( "xiaoming" )   # 调用类方法
print  mc.func2()
  
# python test.py
100
Hello xiaoming!
100

上面示例中,x变量称为类属性,类属性又分为类属性和实例属性:

1)类属性属于类本身,通过类名访问,一般作为全局变量。比如mc.x

2)如果类方法想调用类属性,需要使用self关键字调用。比如self.x

3)实例属性是实例化后对象的方法和属性,通过实例访问,一般作为局部变量。下面会讲到。

4)当实例化后可以动态类属性,下面会讲到。

类方法调用:

1)类方法之间调用:self.<方法名>(参数),参数不需要加self

2)外部调用:<实例名>.<方法名>

6.3 类的说明

给类添加注释,提高可阅读性,可以通过下面方式查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
方法 1
>>>  class  MyClass:
...    """
...   这是一个测试类.
...   """
...    pass
...
>>>  print  MyClass.__doc__
   这是一个测试类.
>>>
方法 2
>>>  help (MyClass)
Help  on  class  MyClass  in  module __main__:
class  MyClass
  |  这是一个测试类.

6.4 类内置方法

内置方法

描述

__init__(self, ...) 初始化对象,在创建新对象时调用
__del__(self) 释放对象,在对象被删除之前调用
__new__(cls, *args, **kwd) 实例的生成操作,在__init__(self)之前调用
__str__(self) 在使用print语句时被调用,返回一个字符串
__getitem__(self, key) 获取序列的索引key对应的值,等价于seq[key]
__len__(self) 在调用内建函数len()时被调用
__cmp__(str, dst) 比较两个对象src和dst
__getattr__(s, name) 获取属性的值
__setattr__(s, name, value) 设置属性的值
__delattr__(s, name) 删除属性
__gt__(self, other) 判断self对象是否大于other对象
__lt__(self, other) 判断self对象是否小于other对象
__ge__(self, other) 判断self对象是否大于或等于other对象
__le__(self, other) 判断self对象是否小于或等于other对象
__eq__(self, other) 判断self对象是否等于other对象
__call__(self, *args) 把实例对象作为函数调用

6.5 初始化实例属性

很多类一般都有初始状态的,常常定义对象的共同特性,也可以用来定义一些你希望的初始值。

Python类中定义了一个构造函数__init__,对类中的实例定义一个初始化对象,常用于初始化类变量。当类被实例化,第二步自动调用的函数,第一步是__new__函数。

__init__构造函数也可以让类传参,类似于函数的参数。

__init__构造函数使用:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  MyClass():
   def  __init__( self ):
     self .name  =  "xiaoming"
   def  func( self ):
     return  self .name
mc  =  MyClass()
print  mc.func()
     
# python test.py
xiaoming

__init__函数定义到类的开头.self.name变量是一个实例属性,只能在类方法中使用,引用时也要这样self.name。

   类传参:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  MyClass():
   def  __init__( self , name):
      self .name  =  name
   def  func( self , age):
      return  "name: %s,age: %s"  % ( self .name, age)
mc  =  MyClass( 'xiaoming' )   # 第一个参数是默认定义好的传入到了__init__函数
print  mc.func( '22'
     
# python test.py
Name: xiaoming, Age:  22


博客地址:http://lizhenliang.blog.51cto.com

QQ群:Shell/Python运维开发群 323779636


6.6 类私有化(私有属性)

6.6.1 单下划线

实现模块级别的私有化,以单下划线开头的变量和函数只能类或子类才能访问。当from modulename import * 时将不会引入以单下划线卡头的变量和函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  MyClass():
    _age  =  21
    def  __init__( self , name = None ):
       self ._name  =  name
    def  func( self , age):
       return  "Name: %s, Age: %s"  % ( self ._name, age)
mc  =  MyClass( 'xiaoming' )
print  mc.func( '22' )
print  mc._name
print  mc._age
     
# python test.py
Name: xiaoming, Age:  22
xiaoming
21

_age和self._name变量其实就是做了个声明,说明这是个内部变量,外部不要去引用它。

6.6.2 双下划线

以双下划线开头的变量,表示私有变量,受保护的,只能类本身能访问,连子类也不能访问。避免子类与父类同名属性冲突。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  MyClass():
    __age  =  21
    def  __init__( self , name = None ):
       self .__name  =  name
    def  func( self , age):
       return  "Name: %s, Age: %s"  % ( self .__name, age)
mc  =  MyClass( 'xiaoming' )
print  mc.func( '22' )
print  mc.__name
print  mc.__age
     
# python test.py
Name: xiaoming, Age:  22
Traceback (most recent call last):
  File  "test.py" , line  12 in  <module>
print  mc.__name
AttributeError: MyClass instance has no attribute  '__name'

可见,在单下划线基础上又加了一个下划线,同样方式类属性引用,出现报错。说明双下划线变量只能本身能用。

如果想访问私有变量,可以这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  MyClass():
    __age  =  21
    def  __init__( self , name = None ):
       self .__name  =  name
    def  func( self , age):
       return  "Name: %s, Age: %s"  % ( self .__name, age)
mc  =  MyClass( 'xiaoming' )
print  mc.func( '22' )
print  mc._MyClass__name
print  mc._MyClass__age
     
# python test.py
Name: xiaoming, Age:  22
xiaoming
21

self.__name变量编译成了self._MyClass__name,以达到不能被外部访问的目的,并没有真正意义上的私有。

6.6.3 特殊属性(首尾双下划线)

一般保存对象的元数据,比如__doc__、__module__、__name__:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>>  class  MyClass:    
     """
     这是一个测试类说明的类。
     """
     pass
# dic()返回对象内变量、方法
>>>  dir (MyClass)
[ '__doc__' '__module__' ]
>>> MyClass.__doc__
'\n\t\xd5\xe2\xca\xc7\xd2\xbb\xb8\xf6\xb2\xe2\xca\xd4\xc0\xe0\xcb\xb5\xc3\xf7\xb5\xc4\xc0\xe0\xa1\xa3\n\t'
>>> MyClass.__module__
'__main__'
>>> MyClass.__name__
'MyClass'

这里用到了一个新内置函数dir(),不带参数时,返回当前范围内的变量、方法的列表。带参数时,返回参数的属性、方法的列表。

Python自己调用的,而不是用户来调用。像__init__ ,你可以重写。

6.7 类的继承

子类继承父类,子类将继承父类的所有方法和属性,提高代码重用。

1)简单继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  Parent():
    def  __init__( self , name = None ):
       self .name  =  name
    def  func( self , age):
       return  "Name: %s, Age: %s"  % ( self .name, age)
class  Child(Parent):
    pass
mc  =  Child( 'xiaoming' )
print  mc.func( '22' )
print  mc.name
     
# python test.py
Name: xiaoming, Age:  22
xiaoming

2)子类实例初始化

如果子类重写了构造函数,那么父类的构造函数将不会执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  Parent():
    def  __init__( self ):
        self .name_a  =  "xiaoming"
    def  funcA( self ):
        return  "function A: %s"  %  self .name_a
class  Child(Parent):
    def  __init__( self ):
        self .name_b  =  "zhangsan"
    def  funcB( self ):
        return  "function B: %s"  %  self .name_b
mc  =  Child()
print  mc.name_b
print  mc.funcB()
print  mc.funcA()
     
# python test.py
zhangsan
function B: zhangsan
Traceback (most recent call last):
  File  "test2.py" , line  17 in  <module>
  print  mc.funcA()
  File  "test2.py" , line  7 in  funcA
  return  "function A: %s"  %  self .name_a
AttributeError: Child instance has no attribute  'name_a'

抛出错误,提示调用funcA()函数时,没有找到name_a属性,也就说明了父类的构造函数并没有执行。

如果想解决这个问题,可通过下面两种方法:

方法1:调用父类构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  Parent():
    def  __init__( self ):
       self .name_a  =  "xiaoming"
    def  funcA( self ):
       return  "function A: %s"  %  self .name_a
class  Child(Parent):
    def  __init__( self ):
       Parent.__init__( self )
       self .name_b  =  "zhangsan"
    def  funcB( self ):
       return  "function B: %s"  %  self .name_b
mc  =  Child()
print  mc.name_b
print  mc.funcB()
print  mc.funcA()
     
# python test.py
zhangsan
function B: zhangsan
function A: xiaoming

方法2:使用supper()函数继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python    
# -*- coding: utf-8 -*-
class  Parent( object ):
    def  __init__( self ):
       self .name_a  =  "xiaoming"
    def  funcA( self ):
       return  "function A: %s"  %  self .name_a
class  Child(Parent):
    def  __init__( self ):
       super (Child,  self ).__init__()
       self .name_b  =  "zhangsan"
    def  funcB( self ):
       return  "function B: %s"  %  self .name_b
mc  =  Child()
print  mc.name_b
print  mc.funcB()
print  mc.funcA()
     
# python test.py
zhangsan
function B: zhangsan
function A: xiaoming

6.8 多重继承

每个类可以拥有多个父类,如果调用的属性或方法在子类中没有,就会从父类中查找。多重继承中,是依次按顺序执行。

类简单的继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  A:
     def  __init__( self ):
         self .var1  =  "var1"
         self .var2  =  "var2"
     def  a( self ):
         print  "a..."
class  B:
     def  b( self ):
         print  "b..."
class  C(A,B):
     pass
=  C()
c.a()
c.b()
print  c.var1
print  c.var2
 
# python test.py
a...
b...
var1
var2

类C继承了A和B的属性和方法,就可以像使用父类一样使用它。

子类扩展方法,直接在子类中定义即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  A:
     def  __init__( self ):
         self .var1  =  "var1"
         self .var2  =  "var2"
     def  a( self ):
         print  "a..."
class  B:
     def  b( self ):
         print  "b..."
class  C(A,B):
     def  test( self ):
         print  "test..."
=  C()
c.a()
c.b()
c.test()
print  c.var1
print  c.var2
 
# python test.py
a...
b...
test...
var1
var2

在这说明下经典类和新式类。

经典类:默认没有父类,也就是没继承类。

新式类:有继承的类,如果没有,可以继承object。在Python3中已经默认继承object类。

经典类在多重继承时,采用从左到右深度优先原则匹配,而新式类是采用C3算法(不同于广度优先)进行匹配。两者主要区别在于遍历父类算法不同,具体些请在网上查资料。

6.9 方法重载

直接定义和父类同名的方法,子类就修改了父类的动作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  Parent():
     def  __init__( self , name = 'xiaoming' ):
         self .name  =  name
     def  func( self , age):
         return  "Name: %s, Age: %s"  % ( self .name, age)
class  Child(Parent):
     def  func( self , age = 22 ):
         return  "Name: %s, Age: %s"  % ( self .name, age)
mc  =  Child()
print  mc.func()
 
# python test.py
Name: xiaoming, Age:  22

6.10 修改父类方法

在方法重载中调用父类的方法,实现添加功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  Parent():
     def  __init__( self , name = 'xiaoming' ):
         self .name  =  name
     def  func( self , age):
         return  "Name: %s, Age: %s"  % ( self .name, age)
class  Child(Parent):
     def  func( self , age):
         print  "------"
         print  Parent.func( self , age)    # 调用父类方法
         print  "------"
mc  =  Child()
mc.func( '22' )
# python test.py
- - - - - -
Name: xiaoming, Age:  22
- - - - - -

还有一种方式通过super函数调用父类方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  Parent():
     def  __init__( self , name = 'xiaoming' ):
         self .name  =  name
     def  func( self , age):
         return  "Name: %s, Age: %s"  % ( self .name, age)
class  Child(Parent):
     def  func( self , age):
         print  "------"
         print  super (Child,  self ).func(age)
         print  "------"
mc  =  Child()
mc.func( '22' )
# python test.py
- - - - - -
Traceback (most recent call last):
   File  "test2.py" , line  15 in  <module>
     mc.func( '22' )
   File  "test2.py" , line  11 in  func
     print  super (Child,  self ).func(age)
TypeError: must be  type not  classobj

抛出错误,因为super继承只能用于新式类,用于经典类就会报错。

那我们就让父类继承object就可以使用super函数了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  Parent( object ):
     def  __init__( self , name = 'xiaoming' ):
         self .name  =  name
     def  func( self , age):
         return  "Name: %s, Age: %s"  % ( self .name, age)
class  Child(Parent):
     def  func( self , age):
         print  "------"
         print  super (Child,  self ).func(age)    # 调用父类方法。在Python3中super参数可不用写。
         print  "------"
mc  =  Child()
mc.func( '22' )
 
# python test.py
- - - - - -
Name: xiaoming, Age:  22
- - - - - -

6.11 属性访问的特殊方法

有四个可对类对象增删改查的内建函数,分别是getattr()、hasattr()、setattr()、delattr()。

6.11.1 getattr()

返回一个对象属性或方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>>  class  A:    
...    def  __init__( self ):
...      self .name  =  'xiaoming'
...    def  method( self ):
...      print  "method..."
...
>>> c  =  A()
>>>  getattr (c,  'name' 'Not find name!' )   
'xiaoming'
>>>  getattr (c,  'namea' 'Not find name!' )
>>>  getattr (c,  'method' 'Not find method!' )
<bound method A.method of <__main__.A instance at  0x93fa70 >>
>>>  getattr (c,  'methoda' 'Not find method!' )
'Not find method!'

6.11.2 hasattr()

判断一个对象是否具有属性或方法。返回一个布尔值。

1
2
3
4
5
6
7
8
>>>  hasattr (c,  'name' )    
True
>>>  hasattr (c,  'namea' )
False
>>>  hasattr (c,  'method' )
True
>>>  hasattr (c,  'methoda' )
False

6.11.3 setattr()

给对象属性重新赋值或添加。如果属性不存在则添加,否则重新赋值。

1
2
3
4
5
6
7
>>>  hasattr (c,  'age' )    
False
>>>  setattr (c,  'age' 22 )
>>> c.age
22
>>>  hasattr (c,  'age' )
True

6.11.4 delattr()

删除对象属性。

1
2
3
>>>  delattr (c,  'age' )    
>>>  hasattr (c,  'age' )             
False

6.12 类装饰器

与函数装饰器类似,不同的是类要当做函数一样调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python
# -*- coding: utf-8 -*-
class  Deco:
     def  __init__( self , func):
        self ._func  =  func
        self ._func_name  =  func.__name__
     def  __call__( self ):
        return  self ._func(),  self ._func_name
@Deco
def  f1():
     return  "Hello world!"
print  f1()
 
# python test.py
( 'Hello world!' 'f1' )

6.13 类内置装饰器

下面介绍类函数装饰器,在实际开发中,感觉不是很常用。

6.10.1 @property

@property属性装饰器的基本功能是把类中的方法当做属性来访问。

在没使用属性装饰器时,类方法是这样被调用的:

1
2
3
4
5
6
7
8
9
10
11
12
>>>  class  A:    
...     def  __init__( self , a, b):
...       self .a  =  a
...       self .b  =  b
...     def  func( self ):
...       print  self .a  +  self .b
...
>>> c  =  A( 2 , 2 )
>>> c.func()
4
>>> c.func
<bound method A.func of <__main__.A instance at  0x7f6d962b1878 >>

使用属性装饰器就可以像属性那样访问了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>>  class  A:    
...      def  __init__( self , a, b):
...        self .a  =  a
...        self .b  =  b
...     @ property
...      def  func( self ):
...        print  self .a  +  self .b
...
>>> c  =  A( 2 , 2 )
>>> c.func
4
>>> c.func()
4
Traceback (most recent call last):
  File  "<stdin>" , line  1 in  <module>
TypeError:  'NoneType'  object  is  not  callable

6.10.2 @staticmethod

@staticmethod是静态方法装饰器,可以通过类对象访问,也可以通过实例化后类对象实例访问。

实例方法的第一个参数是self,表示是该类的一个实例,称为类对象实例。

而使用静态方法装饰器,第一个参数就不用传入实例本身(self),那么这个方法当做类对象,由Python自身处理。

看看普通方法的用法:

1
2
3
4
5
6
7
>>>  class  A:                         
...    def  staticMethod ( self ):   
...       print  "not static method..."
...
>>> c  =  A()         
>>> c. staticMethod ()
not  static method...

使用静态方法则是这么用:

1
2
3
4
5
6
7
8
9
10
>>>  class  A:                       
...   @ staticmethod             
...    def  staticMethod ():       
...      print  "static method..."
...
>>> A. staticMethod ()    # 可以通过类调用静态方法
static method...
>>> c  =  A()   
>>> c. staticMethod ()    # 还可以使用普通方法调用
static method...

静态方法和普通的非类方法作用一样,只不过命名空间是在类里面,必须通过类来调用。一般与类相关的操作使用静态方法。

6.10.3 @classmethod

@classmethod是类方法装饰器,与静态方法装饰器类似,也可以通过类对象访问。主要区别在于类方法的第一个参数要传入类对象(cls)。

1
2
3
4
5
6
7
8
9
>>>  class  A:                       
...   @ classmethod             
...    def  classMethod ( cls ):   
...      print  "class method..."
...      print  cls .__name__
...
>>> A. classMethod ()
class  method...
A

6.14 __call__方法

可以让类中的方法像函数一样调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
>>>  class  A:
...    def  __call__( self , x): 
...      print  "call..."
...      print  x
...
>>> c  =  A()
>>> c( 123 )
call...
123
>>>  class  A:
...    def  __call__( self * args,  * * kwargs):
...       print  args
...       print  kwargs
...
>>> c  =  A()
>>> c( 1 , 2 , 3 ,a = 1 ,b = 2 ,c = 3 )
( 1 2 3 )
{ 'a' 1 'c' 3 'b' 2 }

猜你喜欢

转载自blog.csdn.net/u012361418/article/details/72842595