Python面向对象

2/17/2021 面向对象

面向对象(Object Oriented)是一种编程思想。在当下,它已经成为一种重要的编程思想。对象是一种对现实世界抽象的表达,它能使计算机程序设计变得更加灵活,提高重用性和扩展性等。在面向对象的思想里,万物皆对象。

为了更好理解面向对象,可以将其与传统面向过程比较。在面向过程中,程序视为一系列变量和函数集合或者一系列对计算机下达指令。在面向对象中,对象是程序基本单元,每个对象都应该能够接收数据,处理数据和传达数据,而对象是类的实例。类是对高度抽象,类似于设计图纸。

面向过程程序基本工具:

  • 变量:存储一些内置类的信息如整数和字符等,也可以是数据结构如字符串,列表,树图等或者是复合的变量如指针

  • 程序(方法,函数):是指输入数据产生输出结构。如程序循环,条件和结构化编程结构

    典型的面向过程语言主要是C语言。

面向对象基本概念:

  • 类:定义现实事物抽象特点,主要包括数据的形式和数据的操作

  • 对象:类的实例

    典型的面向对象语言主要是Java,Python,C#,C++,PHP,Swift,Perl,Objective-C等。

具体详细了解,可参考传送门 (opens new window)

# 类和对象

类和对象是面向对象中最基本也是最重要两个概念。类是具有相同属性和行为的对象集合(抽象),即定义了对象的属性和方法。而对象是类的实例。举个例子来说,飞机的设计图纸是类,而实际生产的飞机是对象。我们通过类创建对象(类实例化对象),就像根据飞机的设计图纸来生产飞机。

类和对象举例1 类和对象举例2

#

  • 定义类

    在Python中定义类,主要使用class关键字。

    语法:

    class ClassName:
        '''类的描述信息'''            # 类的描述信息
        statement                    # 类体
    
    1
    2
    3

    参数说明:

    • ClassName:用于指定类名,主要采用“驼峰式命名法”。

    • statement:类体,主要由类变量(或类成员)、方法和属性等定义语句组成。如果在定义类时,没想好类的具体功能,可以在类体中直接使用pass语句代替。

    实例:

    class Song:
        '''歌曲类'''
        pass
    
    1
    2
    3
  • 类的构成

    一个类主要由三部分构成。类名,类的属性和类的方法。

    实例:

    歌曲类

    • 类名:歌曲(Song)
    • 属性:歌曲名(name),歌曲大小(size),歌曲时长(time)等
    • 方法:播放(Play),暂停(Stop)
    class Song(object):
        def __init__(self,name,size,time):
            self.name = name
            self.size = size
            self.time = time
        
        def Play(self):
            pass
        
        def Stop(self):
            pass
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    说明:在类中方法的定义和普通的函数定义是一致的,只有一点不同,就是类的方法(函数)定义第一个参数永远是self。self是代表类的实例,而非类,即对象本身,这点与Java,C#,C/C++中this的概念是一致的。在构造方法中为类绑定了其属性。__init__()是特殊方法中构造方法,在后面会详细说明。

# 对象

  • 创建实例对象

    在定义类之后,我们还需要创建类的实例即对象,就像有了飞行图纸,并不代表你具有了一架飞机,但是你可以通过设计图纸来制造一架飞行。这个制造过程就是创建实例对象。

    实例化对象在其他语言中一般使用new关键字,但Python中没有这个关键字,类的实例化类似于函数调用。

    语法:

    obj = ClassName(parameterlist)
    
    1

    参数说明:

    • ClassName 是指定具体的类
    • parameterlist 是可选参数,当创建一个类时,没有创建__init__()方法,或者__init__ 方法只有一个self 参数时,parameterlist 可以省略。

    实例:

    class Song(object):
        pass
    
    song1 = Song()
    print(song1)
    
    1
    2
    3
    4
    5
  • 访问属性和调用方法

    当有一个对象后,我们可以通过点号.来访问对象的属性和方法。

    实例:

    class Song(object):
        def __init__(self,name,size,time):
            self.name = name
            self.size = size
            self.time = time
        
        def Play(self):
            print('开始播放歌曲:'+self.name)
        
        def Stop(self):
            print('停止播放歌曲:'+self.name)
           
    song1 = Song('平凡之路','5M','5:02')
    print('歌曲名:{0},歌曲大小:{1},歌曲时长:{2}'.format(song1.name,song1.size,song1.time))
    song1.Play()
    song1.Stop()
    
    歌曲名:平凡之路,歌曲大小:5M,歌曲时长:5:02
    开始播放歌曲:平凡之路
    停止播放歌曲:平凡之路
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

# 类属性和实例属性

类的属性主要分为两种,类属性和实例属性。在前面的例子中,我们定义都是实例属性(对象属性)。

类属性,顾名思义,就是类所有用的属性,它的一大特点就是被所有类的实例对象共有,内存中只存在一个副本。与Java,C#,C/C++中静态成员变量类似的。

实例属性,就是所有类的实例对象所私有的,每一个类的实例对象在实例化时所赋予的属性,都是不同的。

实例:

class Student(object):
    banji = '高一262' #类属性
    def __init__(self,name,age,gender):
        self.name = name #实例属性 
        self.age = age   #实例属性
        self.gender = gender #实例属性
    
    def learn(self):
        pass
    
s1 = Student('snake8859',16,'boy')
s2 = Student('prince',15,'girl')
print(s1.banji) #高一262 可以通过实例对象调用
print(s2.banji) #高一262 
print(Student.banji) #高一262 可以通过类对象调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

说明:类属性访问即可以通过实例对象访问,也可以通过类名.类属性访问。而且多个实例对象中类属性值是共有的。

# 类方法,静态方法和实例方法

类的方法主要分三种,类方法,静态方法和实例方法。在前面的例子中,我们定义都是实例方法。

  • 类方法

    类方法是类对象所拥有的方法,需要用修饰器@classmethod来标识其为类方法。对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数。能够通过实例对象和类对象去访问

    实例:

    class People(object):
        country = 'china'
    
        #类方法,用classmethod来进行修饰
        @classmethod
        def getCountry(cls):
            return cls.country
    
    p = People()
    print(p.getCountry())   #可以用过实例对象引用
    print(People.getCountry())  #可以通过类对象引用
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
  • 静态方法

    静态方法需要通过修饰器@staticmethod来进行修饰,静态方法不需要多定义参数。

    实例:

    class People(object):
        country = 'china'
    
        @staticmethod
        #静态方法
        def getCountry():
            return People.country
        
    print(People.getCountry())
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

从类方法和实例方法以及静态方法的定义形式就可以看出来,类方法的第一个参数是类对象cls,那么通过cls引用的必定是类对象的属性和方法;而实例方法的第一个参数是实例对象self,那么通过self引用的可能是类属性、也有可能是实例属性;静态方法中不需要额外定义参数,因此在静态方法中引用类属性的话,必须通过类对象来引用。

# "特殊"方法

在Python中当方法(函数)名为__XXX__时表示这类方法是特殊方法。其中有很多特殊方法,暂时只介绍以下这些常用的特殊方法。

  • 构造方法__init__()

    构造方法是指当类实例化对象时所执行的方法,即每当创建一个类的新实例时,都会自动执行该方法。一般主要用于初始化对象属性。与Java,C#,C/C++中构造函数是一个概念,只不过语法表达形式不一样。

    实例:

    class Point:
       def __init__( self, x=0, y=0):
          self.x = x
          self.y = y
          print('点的坐标为({0},{1})'.format(self.x,self.y))
    p1 = Point()
    p1 = Point(2,2)
    
    点的坐标为(0,0)
    点的坐标为(2,2)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  • 析构方法__del__()

    析构方法是指当对象销毁时所执行的方法,即每当销毁一个类的实例时,都会自动执行该方法。一般用于释放资源等。与Java,C#,C/C++中析构函数是一个概念,只不过语法表达形式不一样。

    实例:

    class Point:
        #构造函数
       def __init__(self, x=0, y=0):
          self.x = x
          self.y = y
          print('点的坐标为({0},{1})'.format(self.x,self.y))
       
       #析构函数
       def __del__(self):
          class_name = self.__class__.__name__
          print(class_name, "销毁")
        
    p1 = Point()
    p2 = Point(2,2)
    
    del p1
    del p2
    
    点的坐标为(0,0)
    点的坐标为(2,2)
    Point 销毁
    Point 销毁
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
  • 对象打印方法__str()__

    对象打印方法定义后,每次print()对象名,就不会再输出对象的内存地址,而是对象打印方法中的内容。一般用于方便调试对象。

    实例:

    class Point:
        #构造函数
       def __init__(self, x=0, y=0):
          self.x = x
          self.y = y
       
       #对象打印函数
       def __str__(self):
          return '点的坐标为({0},{1})'.format(self.x,self.y)
    
    p1 = Point()
    p2 = Point(2,2)
    print(p1)
    print(p2)
    
    点的坐标为(0,0)
    点的坐标为(2,2)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17

    注意:函数需要return返回值

# 面向对象特点

面向对象三大基本特点,封装,继承和多态。

# 封装

封装是面向对象的核心思想,所谓封装就是将对象的属性和行为封装起来,其载体就是类,简单来说,就是将细节隐藏,仅暴露入口和出口供使用。比如,人们使用计算机,只需要利用键盘和鼠标即可完成大部分工作,而不需要明白计算机内部是如何完成这些工作的,现实中很多的例子都是封装思想的体现。

  1. 访问限制

访问限制可以算是封装思想的体现之一。在定义了类的属性和方法后,外部代码可以通过实例对象直接操作数据和方法。但是我们有时候不想让一些内部属性被外部访问,这时候就需要对属性和方法进行访问限制。在Java中,我们可以通过访问修饰符private,default,protected,public来控制访问限制。在Python则是通过__的方式实现。

  • 类的私有属性

    __private_attrs:两个下划线开头,声明该属性为私有,不能在类的外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs

  • 类的私有方法

    __private_method:两个下划线开头,声明该方法为私有方法,不能在类的外部调用。在类的内部调用 self.__private_methods

实例:

class Bank:
    country = 'China' #公开属性
    __money = 20 	  #私有属性
    
    #构造函数
    def __init__(self,bankName,bankAddress):
        self.bankName = bankName #公开属性
        self.__bankAddress = bankAddress #私有属性
    
    #公开方法
    def printBankBaseInfo(self):
        print("银行的名称:"+self.bankName)
    
    #私有方法
    def __printBankInfo(self):
        print("银行的存储金额为:{0},银行地址为:{1}".format(self.__money,self.__bankAddress))
        
        
    
b1 = Bank('银行One','长沙')
b2 = Bank('银行Two','广州')

#访问公开属性和方法
print(b1.country,b1.bankName) #China 银行One
print(b2.country,b2.bankName) #China 银行Two
b1.printBankBaseInfo() #银行的名称:银行One
b2.printBankBaseInfo() #银行的名称:银行Two

#访问私有属性和方法
print(b1.__money) #AttributeError异常
print(b2.__money) #AttributeError异常
print(b1.__bankAddress) #AttributeError异常
print(b2.__bankAddress) #AttributeError异常
b1.__printBankInfo() #AttributeError异常
b2.__printBankInfo() #AttributeError异常
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
28
29
30
31
32
33
34
35

说明:从实例中可以看出,对于公开属性和方法,我们可以直接调用,但是对于私有属性和方法不能直接调用。这样可以有效保护某些类的私有属性不被任意的篡改导致程序发生不可预期的错误。

如果外部代码一定要获取私有属性怎么办呢?我们可以通过增加get_XXX()set__XX()这样的方法。

class Bank:
    country = 'China' #公开属性
    __money = 20 	  #私有属性
    
    #构造函数
    def __init__(self,bankName,bankAddress):
        self.bankName = bankName #公开属性
        self.__bankAddress = bankAddress #私有属性
    
    #公开方法
    def printBankBaseInfo(self):
        print("银行的名称为"+self.bankName)
    
    #私有方法
    def __printBankInfo(self):
        print("银行的存储金额为:{0},银行地址为:{1}".format(self.__money,self.__bankAddress))
        
    
    def get_bankAddress(self):
        return self.__bankAddress

    def set_bankAddress(self,userId,bankAddress):
        if(userId =='银行工作人员'):
            self.__bankAddress = bankAddress
        else:
            print('您无权限修改银行地址')

b1 = Bank('银行One','长沙')
b2 = Bank('银行Two','广州')

#访问私有属性和方法
print(b1.get_bankAddress()) #长沙
print(b2.get_bankAddress()) #广州


b1.set_bankAddress('非银行工作人员','贵州') #您无权限修改银行地址
b2.set_bankAddress('银行工作人员','揭阳') #您已修改银行地址

print(b1.get_bankAddress()) #长沙
print(b2.get_bankAddress()) #揭阳
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
28
29
30
31
32
33
34
35
36
37
38
39
40

那么一定有感觉奇怪,为什么要大费周折定义两个函数去控制私有属性,既然你要访问和设置,为什么不直接设置成公开属性。因为在这两个方法中,我们可以私有属性访问和设置之前进行参数检查,保证私有属性不会被任意修改,避免传入无效参数。就如例子中修改银行地址一样,只有具有银行工作人员权限的人才能修改私有属性,而一般权限无法修改,这样保证私有属性的安全性。

补充:私有属性和方法,其实是可以直接访问的,在Python中,可以使用

object._className__attrName对象名._类名__私有属性名

object._className__methodName对象名._类名__私有方法名

class Bank:
    country = 'China' #公开变量
    __money = 20 	  #私有变量
    
    #构造函数
    def __init__(self,bankName,bankAddress):
        self.bankName = bankName
        self.__bankAddress = bankAddress
    
    #公开方法
    def printBankBaseInfo(self):
        print("银行的名称为"+self.bankName)
    
    #私有方法
    def __printBankInfo(self):
        print("银行的存储金额为:{0},银行地址为:{1}".format(self.__money,self.__bankAddress))
        
    
    def get_bankAddress(self):
        return self.__bankAddress

    def set_bankAddress(self,userId,bankAddress):
        if(userId =='银行工作人员'):
            self.__bankAddress = bankAddress
            print('您已修改银行地址')
        else:
            print('您无权限修改银行地址')
b1 = Bank('银行One','长沙')
b2 = Bank('银行Two','广州')

#访问私有属性和方法
print(b1._Bank.__bankAddress) #长沙
print(b2._Bank.__bankAddress) #广州
b1._Bank__printBankInfo() #银行的存储金额为:20,银行地址为:长沙
b2._Bank__printBankInfo() #银行的存储金额为:20,银行地址为:广州
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
28
29
30
31
32
33
34
35

# 继承

继承也是面向对象核心思想。因为面向对象编程的好处之一是代码的重用,而实现这个重用的方式之一就是继承。在定义一个类之后,我们可以从某些现有的类继承。新的类称为子类(基类),而被继承的类称为父类(超类),如同我们继承父母辈身上一些体貌特征,父母是父类,我们就是子类,子类不一定完全与父类一致,也可以拥有自己的特点,就像我们每一个人又不同与父母,有自己的一些特征,这些特征是独有的。

语法:

class ClassName(baseclasslist)
    '''类的帮助信息'''
    statement
1
2
3

参数说明:

  • ClassName:用于指定类名。
  • baseclasslist:用于指定要继承的基类,可以有多个,类名之间用逗号“,”分隔。如果不指定,将是所有 Python对象的根类object。
  • statement:类体,主要由类变量(或类成员)、方法和属性等定义语句组成。如果在定义类时,没想好类的具体功能,也可以在类体中直接使用pass语句代替。
  1. 单继承

    顾名思义,单继承,就是子类只继承一个父类的情况。例如,猫狗属于动物类的子类,那么猫狗都继承动物。

    同理,不同种类的猫狗又继承与猫狗。

    单继承

    实例:

    #父类
    class Animal(object):
        
        def __init__(self, name):
            self.name = name
            
        def run(self):
            print("{0}--在跑".format(self.name))
    
    #子类
    class Cat(Animal):
    
        def __init__(self, name):
            self.name = name
            
        def eat(self):
           print("{0}--在吃".format(self.name))
    
    c1 = Cat('喵喵')
    c1.run() #喵喵--在跑
    c1.eat() #喵喵--在吃
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    说明:我们可以看到父类拥有run方法,通过子类的继承,使得子类也拥有了run方法,同时子类又实现了eat方法,属于子类独有的方法。

  2. 多继承

    顾名思义,多继承就是子类能继承多个父类的情况。多继承好处是拓展子类功能和减少类的数量增加。这点与Java不同,在Java中不支持多继承机制的,但是Java中有接口机制来弥补多继承。

    具体理解和实例可参考传送门 (opens new window)

  3. 方法重写

    如果从父类继承下来的方法不能满足需求,可以在子类中重写父类的方法。

    实例:

    #父类
    class Animal(object):
        
        def __init__(self, name):
            self.name = name
            
        def run(self):
            print("{0}--在跑".format(self.name))
    
    #子类
    class Cat(Animal):
    
        def __init__(self, name):
            self.name = name
        
        def run(self):
            print("{0}--在墙上跑".format(self.name))
            
        def eat(self):
           print("{0}--在吃".format(self.name))
    
    c1 = Cat('喵喵')
    c1.run() #喵喵--在墙上跑
    c1.eat() #喵喵--在吃
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    说明:在子类中重新定义父类的run方法,这种做法称为方法重写。在子类执行run方法时,就不会再调用父类的run方法,而是子类重写的run方法。

  4. 子类继承父类构造函数说明

    子类不重写__init__,实例化子类时,会自动调用父类定义的 __init__

    实例:

    class Father(object):
        def __init__(self, name):
            self.name=name
            print('调用父类构造函数')
            print ( "name: %s" %( self.name) )
            
        def getName(self):
            return 'Father ' + self.name
     
    class Son(Father):
        def getName(self):
            return 'Son '+self.name
     
    son=Son('A')
    print ( son.getName() )
    
    调用父类构造函数
    name: A
    Son A
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    子类重写__init__时,实例化子类时,不会调用父类已经定义的__init__

    实例:

    class Father(object):
        def __init__(self, name):
            self.name=name
            print('调用父类构造函数')
            print ( "name: %s" %( self.name) )
            
        def getName(self):
            return 'Father ' + self.name
     
    class Son(Father):
        def __init__(self, name):
            self.name=name
            print('调用子类构造函数')
            print ( "name: %s" %( self.name) )
        def getName(self):
            return 'Son '+self.name
     
    son=Son('A')
    print ( son.getName() )
    
    调用子类构造函数
    name: A
    Son A
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    子类重写__init__时,又要调用父类的构造方法,可以使用super关键字。

    super(子类,self).__init__(参数1,参数2....)
    
    1

    实例:

    class Father(object):
        def __init__(self, name):
            self.name=name
            print('调用父类构造函数')
            print ( "name: %s" %( self.name) )
            
        def getName(self):
            return 'Father ' + self.name
     
    class Son(Father):
        def __init__(self, name):
            super(Son, self).__init__(name)
            self.name=name
            print('调用子类构造函数')
            print ( "name: %s" %( self.name) )
        def getName(self):
            return 'Son '+self.name
     
    son=Son('A')
    print ( son.getName() )
    
    调用父类构造函数
    name: A
    调用子类构造函数
    name: A
    Son A
    
    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

# 多态

多态,从字面上意思来理解,是指多种形态。那么是谁的多种形态的呢?我个人理解,在面向对象里是指对象的多种形态,即对象在不同代码环境下表现出不同状态。举个例子,水陆两栖战车,在水面上轮胎会转化为水行形态,在陆地上轮胎会转化为陆行形态,对于轮胎而言,这种表现就是多态。又或者在程序里加号运算符的多态表现。当加号运算符左右两侧变量为int型的时候,它会进行加法运算,当加号运算符左右两侧变量为string类的时候,它会进行拼接运算,也就是说加号运算符在不同的变量类型下,表现出不同的形态。

实例:

class Animal(object):
    def run(self):
        print('Animal is running...')
 
class Dog(Animal):
    def run(self):
        print('Dog is running')
 
class Cat(Animal):
    def run(self):
        print('Cat is running')
  
def AnimalRun(animal):
    animal.run()

 
dog = Dog()
AnimalRun(dog) #Dog is running

cat = Cat()
AnimalRun(cat) #Cat is running
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

说明:以继承关系中多态为例,对于AnimalRun方法而言,如果你传入的是狗类,那么就会运行狗对象的run方法,如果你传入的是猫类,那么就会运行猫对象的run方法,这就是AnimalRun方法的多态表现,在运行时根据对象的确切类型决定运行的行为。调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。

这就是著名的“开闭”原则:

对扩展开放:允许新增Animal子类;

对修改封闭:不需要修改依赖Animal类型的AnimalRun()等函数。

  1. 鸭子类型

    那么什么是鸭子类型呢?在上述实例其实存在一个问题,因为python是动态语言,你传入什么参数,它就是什么类型,也就是说在参数层面上对类型并没有限制,那么我只要这个类型有run方法,其实代码也能运行。这就是动态语言所谓的'鸭子类型':一个事物,看起来像鸭子,走起来像鸭子,那它就是鸭子。

    实例:

    class Animal(object):
        def run(self):
            print('Animal is running...')
     
    class Dog(Animal):
        def run(self):
            print('Dog is running')
     
    class Cat(Animal):
        def run(self):
            print('Cat is running')
    
    class Person():
        def run(self):
            print('Person is running')
    
            
    def AnimalRun(animal):
        animal.run()
    
     
    dog = Dog()
    AnimalRun(dog) #Dog is running
    
    cat = Cat()
    AnimalRun(cat) #Cat is running
    
    person = Person()
    AnimalRun(person) #Person is running
    
    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
    28
    29

    说明:Person类,并没有继承Animal类,但是它因为具有run方法,所以在调用AnimalRun时,它依然可以运行,这个Person类就是所谓鸭子类型。

    补充:出现这种现象主要是动态语言和静态语言之间的差异导致。对于静态语言(Java)来说,如果需要参数传入Animal类型,那么传入的参数对象必须是Animal类型或者其子类,否则无法调用run方法。但是对于Python这样的动态语言而言,对传入参数并没有类型的限制,只要保证有run方法即可。

    如果为了避免鸭子类型的话,那么可以在代码层面进行限制

    def AnimalRun(animal):
        if isinstance(animal, Animal):
            animal.run()
    
    1
    2
    3

    通过判断传入参数类型,来规避鸭子类型。

具体内容可以参考传送门1 (opens new window)传送门2 (opens new window)传送门3 (opens new window)

# 面向对象高级内容

面向对象里进阶的内容梳理于此。

# @xxx(装饰器)

装饰器是指在代码运行期间动态增强功能的方式。具体装饰器内容可参考传送门 (opens new window)

  1. @property

    @property用于将一个方法变成属性调用。在之前了解封装过程中,我们知道对私有属性访问和设置,可以通过定义get_XXX()和set__XX()的方式。

    class Bank:
       
        #构造函数
        def __init__(self,bankName,bankAddress):
            self.bankName = bankName #公开属性
            self.__bankAddress = bankAddress #私有属性
        
        #公开方法
        def printBankBaseInfo(self):
            print("银行的名称为"+self.bankName)
        
        @property
        def bankAddress(self):
            return self.__bankAddress
    
        @bankAddress.setter
        def bankAddress(self,userId,bankAddress):
            if(userId =='银行工作人员'):
                self.__bankAddress = bankAddress
            else:
                print('您无权限修改银行地址')
    
    b1 = Bank('银行One','长沙')
    b2 = Bank('银行Two','广州')
    
    #访问私有属性和方法
    print(b1.bankAddress) #长沙
    print(b2.bankAddress) #广州
    
    b1.set_bankAddress('非银行工作人员','贵州') #您无权限修改银行地址
    b2.set_bankAddress('银行工作人员','揭阳') #您已修改银行地址
    
    print(b1.get_bankAddress()) #长沙
    print(b2.get_bankAddress()) #揭阳
    
    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
    28
    29
    30
    31
    32
    33
    34

    除此之外,python提供@property装饰器,我们可以这样再简化一下。

    class Bank:
       
        #构造函数
        def __init__(self,bankName,bankAddress):
            self.bankName = bankName #公开属性
            self.__bankAddress = bankAddress #私有属性
        
        #公开方法
        def printBankBaseInfo(self):
            print("银行的名称为"+self.bankName)
        
        @property
        def bankAddress(self):
            return self.__bankAddress
    
        @bankAddress.setter
        def bankAddress(self,paramer):
            if(paramer[0] =='银行工作人员'):
                self.__bankAddress = paramer[1]
            else:
                print('您无权限修改银行地址')
    
    b1 = Bank('银行One','长沙')
    b2 = Bank('银行Two','广州')
    
    #访问私有属性和方法
    print(b1.bankAddress) #长沙
    print(b2.bankAddress) #广州
    b1.bankAddress = ['非银行工作人员','贵州']
    b2.bankAddress = ['银行工作人员','揭阳']
    print(b1.bankAddress) #长沙
    print(b2.bankAddress) #揭阳
    
    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
    28
    29
    30
    31
    32

    注意:setter里多个参数的话,需要使用列表或者元组

    这个地方,按个人习惯吧,因为之前Java就是一直用set和get的比较习惯这种,就感觉不太需要@property装饰器。

  2. @classmethod

    @classmethod用于修饰函数方法为类方法的装饰器

  3. @staticmethod

    @staticmethod用于修饰函数方法为静态方法的装饰器

# 设计模式

23种设计模式梳理(Python版)

Last Updated: 11/21/2022, 10:03:43 PM