首页 > 编程语言 >9.Python3 面向对象

9.Python3 面向对象

时间:2024-03-05 13:44:21浏览次数:21  
标签:__ name self 面向对象 实例 print 方法 Python3

Python3 面向对象

1.类定义

Python 中定义一个类使用 class 关键字实现,其基本语法格式如下:

class ClassName:
    <statement-1>
    .
    .
    .
    <statement-N>
  • 类实例化后,可以使用其属性,创建一个类之后,可以通过类名访问其属性。
  • 无论是属性还是方法,对于类来说,它们都不是必需的,可以有也可以没有。另外,Python 类中属性和方法所在的位置是任意的,即它们之间并没有固定的前后次序。
  • 和变量名一样,类名本质上就是一个标识符,因此我们在给类起名字时,必须让其符合 Python 的语法。
  • 同属一个类的所有属性和方法,要保持统一的缩进格式,通常统一缩进 4 个空格。

2.__init__()构造方法

在创建类时,我们可以手动添加一个 __init__() 方法,该方法是一个特殊的实例方法,称为构造方法(或构造函数)。构造方法用于创建对象时使用,每当创建一个类的实例对象时,Python解释器都会自动调用它。

手动添加构造方法的语法格式如下:

def __init__(self,...):
    代码块

注意,此方法的方法名中,开头和结尾各有 2 个下划线,且中间不能有空格。__init__() 方法可以包含多个参数,但必须包含一个名为 self 的参数,且必须作为第一个参数。也就是说,类的构造方法最少也要有一个 self 参数。即便不手动为类添加任何构造方法,Python 也会自动为类添加一个仅包含 self 参数的构造方法。仅包含 self 参数的 __init__() 构造方法,又称为类的默认构造方法。

3.类变量(静态变量)、实例变量和局部变量

在类体中,根据变量定义的位置不同,以及定义的方式不同,变量又可细分为以下 3 种类型:

  • 类体中、所有函数之外:此范围定义的变量,称为类变量(静态变量);
  • 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例变量;
  • 类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。

类变量(静态变量)

类变量的特点是,所有类的实例化对象都同时共享类变量,也就是说,类变量在所有实例化对象中是作为公用资源存在的。类变量的调用方式有 2 种,既可以使用类名直接调用,也可以使用类的实例化对象调用。

通过类名修改类变量或动态添加类变量,会作用到所有的实例化对象。通过对象是无法修改类变量或动态添加类变量的。通过对象对类变量赋值,其本质将不再是修改类变量的值,而是在给该对象定义新的实例变量。实例变量和类变量可以同名,但这种情况下使用对象将无法调用类变量,它会首选实例变量,这也是不推荐“类变量使用对象名调用”的原因。

Python中的静态变量和Java中的静态变量的区别是,Python类体中、所有函数之外的变量就是静态变量,Java中类体中、所有函数之外的变量是实例变量,需要加上static 关键字修饰才是静态变量。

实例变量

实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。

class CLanguage :
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self):
        self.catalog = 13
        
clang = CLanguage()
print(clang.name)
print(clang.add)
#由于 clang 对象未调用 say() 方法,因此其没有 catalog 变量,下面这行代码会报错
#print(clang.catalog)
clang2 = CLanguage()
print(clang2.name)
print(clang2.add)
#只有调用 say(),才会拥有 catalog 实例变量
clang2.say()
print(clang2.catalog)

此 CLanguage 类中,name、add 以及 catalog 都是实例变量。其中,由于 __init__() 函数在创建对象时会自动调用,而 say() 方法需要对象手动调用。因此,CLanguage 类的对象都会包含 name 和 add 实例变量,而只有调用了 say() 方法的对象,才包含 catalog 实例变量。和类变量不同,通过某个对象修改实例变量的值,不会影响类的其它实例化对象,更不会影响同名的类变量。实例变量在函数执行前并不存在,在函数执行完成后,实例变量不会被销毁。

局部变量

除了实例变量,类方法中还可以定义局部变量。和前者不同,局部变量直接以“变量名=值”的方式进行定义,例如:

class CLanguage :
    # 下面定义了一个say实例方法
    def count(self,money):
        sale = 0.8*money
        print("优惠后的价格为:",sale)
clang = CLanguage()
clang.count(100)

通常情况下,定义局部变量是为了所在类方法功能的实现。局部变量只能用于所在函数中,函数执行完成后,局部变量也会被销毁。

4.实例方法、静态方法和类方法

  • 不用任何修改的方法为实例方法
  • 采用 @classmethod 修饰的方法为类方法
  • 采用 @staticmethod 修饰的方法为静态方法

实例方法

通常情况下,在类中定义的方法默认都是实例方法。类的构造方法理论上也属于实例方法,只不过它比较特殊。

class CLanguage:
    #类构造方法,也属于实例方法
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    # 下面定义了一个say实例方法
    def say(self):
        print("正在调用 say() 实例方法")

实例方法最大的特点就是,它最少也要包含一个 self 参数,用于绑定调用此方法的实例对象(Python 会自动完成绑定)。实例方法通常会用对象直接调用,例如:

clang = CLanguage()
clang.say()

Python 也支持使用类名调用实例方法,但此方式需要手动给 self 参数传值。例如:

#类名调用实例方法,需手动给 self 参数传值
clang = CLanguage()
CLanguage.say(clang)
# self 参数需要的是方法的实际调用者(是类对象),而这里只提供了类名,当然无法自动传值。

类方法

Python 类方法和实例方法相似,它最少也要包含一个参数,只不过类方法中通常将其命名为 cls,Python 会自动将类本身绑定给 cls 参数(注意,绑定的不是对象)。也就是说,我们在调用类方法时,无需显式为 cls 参数传参。和 self 一样,cls 参数的命名也不是规定的(可以随意命名),只是 Python 程序员约定俗称的习惯而已。

和实例方法最大的不同在于,类方法需要使用@classmethod修饰符进行修饰,例如:

class CLanguage:
    #类构造方法,也属于实例方法
    def __init__(self):
        self.name = "C语言中文网"
        self.add = "http://c.biancheng.net"
    #下面定义了一个类方法
    @classmethod
    def info(cls):
        print("正在调用类方法",cls)

如果没有 @classmethod,则 Python 解释器会将 fly() 方法认定为实例方法,而不是类方法。类方法推荐使用类名直接调用,当然也可以使用实例对象来调用(不推荐)。

静态方法

静态方法,其实就是我们学过的函数,和函数唯一的区别是,静态方法定义在类这个空间(类命名空间)中,而函数则定义在程序所在的空间(全局命名空间)中。静态方法没有类似 self、cls 这样的特殊参数,因此 Python 解释器不会对它包含的参数做任何类或对象的绑定。

静态方法需要使用@staticmethod修饰,例如:

class CLanguage:
    @staticmethod
    def info(name,add):
        print(name,add)

静态方法的调用,既可以使用类名,也可以使用对象,例如:

#使用类名直接调用静态方法
CLanguage.info("C语言中文网","http://c.biancheng.net")
#使用对象调用静态方法
clang = CLanguage()
clang.info("Python教程","http://c.biancheng.net/python")

在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。

5.self用法详解

在Python中,self是一个表示对象自身的参数,通常作为实例方法的第一个参数。它是一个惯用的名称,但实际上可以使用任何名称,只要它在方法定义中的位置正确即可。self代表类的对象,而非类。

self参数用于访问对象的属性和调用对象的方法。当我们创建一个实例对象时,Python会自动将该实例对象作为self参数传递给方法。这样,在方法中就可以通过self来访问实例对象的属性和调用实例对象的方法。对于构造函数中的 self 参数,其代表的是当前正在初始化的对象。

class Person:
    def __init__(self):
        print("正在执行构造方法")
        print(self,"正在执行构造方法")
    # 定义一个study()实例方法
    def study(self):
        print(self,"正在学Python")
zhangsan = Person()
zhangsan.study()
lisi = Person()
lisi.study()

上面代码中,study() 中的 self 代表该方法的调用者,即谁调用该方法,那么 self 就代表谁。因此,该程序的运行结果为:

正在执行构造方法
<__main__.Person object at 0x0000018BAF91F450> 正在执行构造方法
<__main__.Person object at 0x0000018BAF91F450> 正在学Python    
正在执行构造方法
<__main__.Person object at 0x0000018BAF91F490> 正在执行构造方法
<__main__.Person object at 0x0000018BAF91F490> 正在学Python

除了对象可以直接调用类方法,还有一种函数调用的方式,例如:

class Person:
    def who(self):
        print(self)
zhangsan = Person()
#第一种方式
zhangsan.who()
#第二种方式
who = zhangsan.who
who()#通过 who 变量调用zhangsan对象中的 who() 方法

6.类的实例化(对象)

对已定义好的类进行实例化,其语法格式如下:

类名(参数)

定义类时,如果没有手动添加 __init__() 构造方法,又或者添加的__init__()中仅有一个 self 参数,则创建对象时的参数可以省略不写。

class CLanguage :
    # 下面定义了2个类变量
    name = "C语言中文网"
    add = "http://c.biancheng.net"
    def __init__(self,name,add):
        #下面定义 2 个实例变量
        self.name = name
        self.add = add
        print(name,"网址为:",add)
    # 下面定义了一个say实例方法
    def say(self, content):
        print(content)
# 将该CLanguage对象赋给clanguage变量
clanguage = CLanguage("C语言中文网","http://c.biancheng.net")

在上面的程序中,由于构造方法除 self 参数外,还包含 2 个参数,且这 2 个参数没有设置默认参数,因此在实例化对象时,需要传入相应的 name 值和 add 值(self 参数是特殊参数,不需要手动传值,Python 会自动传给它值)。

对象访问变量或方法

使用已创建好的对象访问类中实例变量的语法格式如下:

对象名.变量名

使用对象调用类中方法的语法格式如下:

对象名.方法名(参数)

7.动态添加属性和方法和类

动态添加属性

在类体中,根据变量定义的位置不同,以及定义的方式不同,变量又可细分为以下 3 种类型:

  • 类体中、所有函数之外:此范围定义的变量,称为类变量(静态变量);
  • 类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例变量;
  • 类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。

为类添加属性,直接加在类上,会使此后所有实例化对象都具备此属性

为实例对象添加属性,仅此实例对象具备此属性,其他实例对象调用此属性会报 no attribute

动态添加方法

方法又可细分为实例方法、静态方法和类方法,Python 语言允许为类动态地添加这 3 种方法;但对于实例对象,则只允许动态地添加实例方法,不能添加类方法和静态方法。为单个实例对象添加方法,不会影响该类的其它实例对象;而如果为类动态地添加方法,则所有的实例对象都可以使用。

class CLanguage:
    pass
#下面定义了一个实例方法
def info(self):
    print("正在调用实例方法")
#下面定义了一个类方法
@classmethod
def info2(cls):
    print("正在调用类方法")
#下面定义个静态方法
@staticmethod
def info3():
    print("正在调用静态方法")
#类可以动态添加以上 3 种方法,会影响所有实例对象
CLanguage.info = info
CLanguage.info2 = info2
CLanguage.info3 = info3
clang = CLanguage()
#如今,clang 具有以上 3 种方法
clang.info()
clang.info2()
clang.info3()
#类实例对象只能动态添加实例方法,不会影响其它实例对象
clang1 = CLanguage()
clang1.info = info
#必须手动为 self 传值
clang1.info(clang1)

程序输出结果为:

正在调用实例方法
正在调用类方法
正在调用静态方法
正在调用实例方法

Python 允许为对象动态增加方法。为对象动态增加的方法,Python 不会自动将调用者自动绑定到第一个参数。

__slots__ 属性

动态给类或者实例对象添加属性或方法,是非常灵活的。但与此同时,如果胡乱地使用,也会给程序带来一定的隐患,即程序中已经定义好的类,如果不做任何限制,是可以做动态的修改的。

Python 提供了 __slots__ 属性,通过它可以避免用户频繁的给实例对象动态地添加属性或方法。__slots__ 只能限制为实例对象动态添加属性和方法,而无法限制动态地为类添加属性和方法。

__slots__ 属性值其实就是一个元组,只有其中指定的元素,才可以作为动态添加的属性或者方法的名称。

class CLanguage:
    __slots__ = ('name','add','info')

可以看到, CLanguage 类中指定了 __slots__ 属性,这意味着,该类的实例对象仅限于动态添加 name、add、info 这 3 个属性以及 name()、add() 和 info() 这 3 个方法。对于动态添加的方法,__slots__ 限制的是其方法名,并不限制参数的个数。

此外,__slots__ 属性对由该类派生出来的子类,也是不起作用的。__slots__ 属性只对当前所在的类起限制作用。如果子类也要限制外界为其实例对象动态地添加属性和方法,必须在子类中设置 __slots__ 属性,若为子类也设置有 __slots__ 属性,那么子类实例对象允许动态添加的属性和方法,是子类中__slots__属性和父类 __slots__ 属性的和。

type()函数:动态创建类

type() 函数的语法格式有 2 种,分别如下:

type(obj) 
type(name, bases, dict)
  • 第一种语法格式用来查看某个变量(类对象)的具体类型,obj 表示某个变量或者类对象。
  • 第二种语法格式用来创建类,其中 name 表示类的名称;bases 表示一个元组,其中存储的是该类的父类;dict 表示一个字典,用于表示类内定义的属性或者方法。

这里重点介绍 type() 函数的另一种用法,即创建一个新类:

#定义一个实例方法
def say(self):
    print("我要学 Python!")
#使用 type() 函数创建类
CLanguage = type("CLanguage",(object,),dict(say = say, name = "C语言中文网"))
#创建一个 CLanguage 实例对象
clangs = CLanguage()
#调用 say() 方法和 name 属性
clangs.say()
print(clangs.name)

Python 元组语法规定,当 (object,) 元组中只有一个元素时,最后的逗号(,)不能省略。

如何判断 dict 字典中添加的是方法还是属性?如果该键值对中,值为普通变量(如 "C语言中文网"),则表示为类添加了一个类属性;反之,如果值为外部定义的函数(如 say() ),则表示为类添加了一个实例方法。

8.封装机制

简单的理解封装(Encapsulation),即在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以“对象.属性名”(或者“对象.方法名(参数)”)的形式调用这些属性(或方法),而只能用未隐藏的方法间接操作这些隐藏的属性和方法。

Python 并没有提供 public、private 这些修饰符。为了实现类的封装,Python 采取了下面的方法:

  • 默认情况下,Python 类中的变量和方法都是公有(public)的,它们的名称前都没有下划线(_);
  • 如果类中的变量和函数,其名称以双下划线“__”开头,则该变量(函数)为私有变量(私有函数),其属性等同于 private

除此之外,还可以定义以单下划线_开头的属性或者方法,这种属性和方法通常被视为私有属性和私有方法,虽然它们也能通过对象正常访问,但这是一种约定俗称的用法,初学者一定要遵守。

例如,如下程序示范了 Python 的封装机制:

class CLanguage :
    def setname(self, name):
        if len(name) < 3:
            raise ValueError('名称长度必须大于3!')
        self.__name = name
    def getname(self):
        return self.__name
    #为 name 配置 setter 和 getter 方法
    name = property(getname, setname)
    def setadd(self, add):
        if add.startswith("http://"):
            self.__add = add
        else:
            raise ValueError('地址必须以 http:// 开头') 
    def getadd(self):
        return self.__add
   
    #为 add 配置 setter 和 getter 方法
    add = property(getadd, setadd)
    #定义个私有方法
    def __display(self):
        print(self.__name,self.__add)
clang = CLanguage()
clang.name = "C语言中文网"
clang.add = "http://c.biancheng.net"
print(clang.name)
print(clang.add)

上面程序中,CLanguage 将 name 和 add 属性都隐藏了起来,但同时也提供了可操作它们的“窗口”,也就是各自的 setter 和 getter 方法,这些方法都是公有(public)的。

property()函数:

正常情况下,类包含的属性应该是隐藏的,只允许通过类提供的方法来间接实现对类属性的访问和操作。因此,在不破坏类封装原则的基础上,为了能够有效操作类中的属性,类中应包含读(或写)类属性的多个 getter(或 setter)方法,这样就可以通过“对象.方法(参数)”的方式操作属性,例如:

class CLanguage:
    #构造函数
    def __init__(self,name):
        self.name = name 
    #设置 name 属性值的函数 
    def setname(self,name):
        self.name = name
    #访问nema属性值的函数
    def getname(self):
        return self.name
    #删除name属性值的函数
    def delname(self):
        self.name="xxx"
clang = CLanguage("C语言中文网")
#获取name属性值
print(clang.getname())
#设置name属性值
clang.setname("Python教程")
print(clang.getname())
#删除name属性值
clang.delname()
print(clang.getname())

Python 中提供了 property() 函数,可以实现在不破坏类封装原则的前提下,让开发者依旧使用“对象.属性”的方式操作类中的属性。property() 函数的基本使用格式如下:

属性名=property(fget=None, fset=None, fdel=None, doc=None) #参数可选

其中,fget 参数用于指定获取该属性值的类方法,fset 参数用于指定设置该属性值的方法,fdel 参数用于指定删除该属性值的方法,最后的 doc 是一个文档字符串,用于说明此函数的作用。

例如,修改上面的程序,为 name 属性配置 property() 函数:

class CLanguage:
    #构造函数
    def __init__(self,n):
        self.__name = n
    #设置 name 属性值的函数
    def setname(self,n):
        self.__name = n
    #访问nema属性值的函数
    def getname(self):
        return self.__name
    #删除name属性值的函数
    def delname(self):
        self.__name="xxx"
    #为name 属性配置 property() 函数
    name = property(getname, setname, delname, '指明出处')
#调取说明文档的 2 种方式
#print(CLanguage.name.__doc__)
help(CLanguage.name)
clang = CLanguage("C语言中文网")
#调用 getname() 方法
print(clang.name)
#调用 setname() 方法
clang.name="Python教程"
print(clang.name)
#调用 delname() 方法
del clang.name
print(clang.name)

9.继承机制

继承机制经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上添加一些成员(属性和方法),但又不想直接将现有类代码复制给新类。也就是说,通过使用继承这种机制,可以轻松实现类的重复使用。

子类继承父类时,只需在定义子类时,将父类(可以是多个)放在子类之后的圆括号里即可。语法格式如下:

class 类名(父类1, 父类2, ...):
    #类定义部分

如果该类没有显式指定继承自哪个类,则默认继承 object 类(object 类是 Python 中所有类的父类,即要么是直接父类,要么是间接父类)。

class People:
    def say(self):
        print("我是一个人,名字是:",self.name)
class Animal:
    def display(self):
        print("人也是高级动物")
#同时继承 People 和 Animal 类
#其同时拥有 name 属性、say() 和 display() 方法
class Person(People, Animal):
    pass
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.say()
zhangsan.display()

运行结果,结果为:

我是一个人,名字是: 张三
人也是高级动物

可以看到,虽然 Person 类为空类,但由于其继承自 People 和 Animal 这 2 个类,因此实际上 Person 并不空,它同时拥有这 2 个类所有的属性和方法。

Python的多继承(不建议使用)

和单继承相比,多继承容易让代码逻辑复杂、思路混乱,一直备受争议,使用多继承经常需要面临的问题是,多个父类中包含同名的类方法。对于这种情况,Python 的处置措施是:根据子类继承多个父类时这些父类的前后次序决定,即排在前面父类中的类方法会覆盖排在后面父类中的同名类方法。

class People:
    def __init__(self):
        self.name = People
    def say(self):
        print("People类",self.name)
class Animal:
    def __init__(self):
        self.name = Animal
    def say(self):
        print("Animal类",self.name)
#People中的 name 属性和 say() 会遮蔽 Animal 类中的
class Person(People, Animal):
    pass
zhangsan = Person()
zhangsan.name = "张三"
zhangsan.say()

程序运行结果为:

People类 张三

可以看到,当 Person 同时继承 People 类和 Animal 类时,People 类在前,因此如果 People 和 Animal 拥有同名的类方法,实际调用的是 People 类中的。

父类方法重写

子类继承了父类,那么子类就拥有了父类所有的类属性和类方法。通常情况下,子类会在此基础上,扩展一些新的类属性和类方法。但凡事都有例外,我们可能会遇到这样一种情况,即子类从父类继承得来的类方法中,大部分是适合子类使用的,但有个别的类方法,并不能直接照搬父类的,如果不对这部分类方法进行修改,子类对象无法使用。针对这种情况,我们就需要在子类中重复父类的方法。

class Bird:
    #鸟有翅膀
    def isWing(self):
        print("鸟有翅膀")
    #鸟会飞
    def fly(self):
        print("鸟会飞")
class Ostrich(Bird):
    # 重写Bird类的fly()方法
    def fly(self):
        print("鸵鸟不会飞")
# 创建Ostrich对象
ostrich = Ostrich()
#调用 Ostrich 类中重写的 fly() 类方法
ostrich.fly()

super()函数:调用父类的构造方法

类的构造方法其实就是实例方法,父类的构造方法,子类同样会继承。Python 是一门支持多继承的面向对象编程语言,如果子类继承的多个父类中包含同名的类实例方法,则子类对象在调用该方法时,会优先选择排在最前面的父类中的实例方法。显然,构造方法也是如此。

在子类中的构造方法中,调用父类构造方法的方式有 2 种,分别是:

  • 类可以看做一个独立空间,在类的外部调用其中的实例方法,可以向调用普通函数那样,只不过需要额外备注类名(此方式又称为未绑定方法)。
  • 使用 super() 函数。但如果涉及多继承,该函数只能调用第一个直接父类的构造方法。也就是说,涉及到多继承时,在子类构造函数中,调用第一个父类构造方法的方式有以上 2 种,而调用其它父类构造方法的方式只能使用未绑定方法。
super().__init__(...)

在掌握 super() 函数用法的基础上,我们可以尝试修改上面的程序:

class People:
    def __init__(self,name):
        self.name = name
    def say(self):
        print("我是人,名字为:",self.name)
class Animal:
    def __init__(self,food):
        self.food = food
    def display(self):
        print("我是动物,我吃",self.food)
class Person(People, Animal):
    #自定义构造方法
    def __init__(self,name,food):
        #调用 People 类的构造方法
        super().__init__(name)
        #super(Person,self).__init__(name) #执行效果和上一行相同
        #People.__init__(self,name)#使用未绑定方法调用 People 类构造方法
        #调用其它父类的构造方法,需手动给 self 传值
        Animal.__init__(self,food)    
per = Person("zhangsan","熟食")
per.say()
per.display()

运行结果为:

我是人,名字为: zhangsan
我是动物,我吃 熟食

Person 类自定义的构造方法中,调用 People 类构造方法,可以使用 super() 函数,也可以使用未绑定方法。但是调用 Animal 类的构造方法,只能使用未绑定方法。

10.多态机制

类的多态特性,满足以下 2 个前提条件:

  • 继承:多态一定是发生在子类和父类之间;
  • 重写:子类重写了父类的方法。
class WhoSay:
    def say(self,who):
        who.say()
class CLanguage:
    def say(self):
        print("调用的是 Clanguage 类的say方法")
class CPython(CLanguage):
    def say(self):
        print("调用的是 CPython 类的say方法")
class CLinux(CLanguage):
    def say(self):
        print("调用的是 CLinux 类的say方法")
a = WhoSay()
#调用 CLanguage 类的 say() 方法
a.say(CLanguage())
#调用 CPython 类的 say() 方法
a.say(CPython())
#调用 CLinux 类的 say() 方法
a.say(CLinux())

程序执行结果为:

调用的是 Clanguage 类的say方法
调用的是 CPython 类的say方法
调用的是 CLinux 类的say方法

此程序中,通过给 WhoSay 类中的 say() 函数添加一个 who 参数,其内部利用传入的 who 调用 say() 方法。这意味着,当调用 WhoSay 类中的 say() 方法时,我们传给 who 参数的是哪个类的实例对象,它就会调用那个类中的 say() 方法。

11.枚举类

如果想将一个类定义为枚举类,只需要令其继承自 enum 模块中的 Enum 类即可。

例如,下面程序演示了如何定义一个枚举类:

from enum import Enum
class Color(Enum):
    # 为序列值指定value值
    red = 1
    green = 2
    blue = 3

在 Color 枚举类中,red、green、blue 都是该类的成员(可以理解为是类变量)。注意,枚举类的每个成员都由 2 部分组成,分别为 name 和 value,其中 name 属性值为该枚举值的变量名(如 red),value 代表该枚举值的序号(序号通常从 1 开始)。

和普通类的用法不同,枚举类不能用来实例化对象,但这并不妨碍我们访问枚举类中的成员。访问枚举类成员的方式有多种,例如以 Color 枚举类为例,在其基础上添加如下代码:

#调用枚举成员的 3 种方式
print(Color.red)
print(Color['red'])
print(Color(1))
#调取枚举成员中的 value 和 name
print(Color.red.value)
print(Color.red.name)
#遍历枚举类中所有成员的 2 种方式
for color in Color:
    print(color)

程序输出结果为:

Color.red
Color.red
Color.red
1
red
Color.red
Color.green
Color.blue

枚举类成员之间不能比较大小,但可以用 == 或者 is 进行比较是否相等,例如:

print(Color.red == Color.green)
print(Color.red.name is Color.green.name)

Flase
Flase

值得一提的是,Python 枚举类中各个成员必须保证 name 互不相同,但 value 可以相同,举个例子:

from enum import Enum
class Color(Enum):
    # 为序列值指定value值
    red = 1
    green = 1
    blue = 3
print(Color['green'])

可以看到,Color 枚举类中 red 和 green 具有相同的值(都是 1),Python 允许这种情况的发生,它会将 green 当做是 red 的别名,因此当访问 green 成员时,最终输出的是 red。

在实际编程过程中,如果想避免发生这种情况,可以借助 @unique 装饰器,这样当枚举类中出现相同值的成员时,程序会报 ValueError 错误。例如:

#引入 unique
from enum import Enum,unique
#添加 unique 装饰器
@unique
class Color(Enum):
    # 为序列值指定value值
    red = 1
    green = 1
    blue = 3
print(Color['green'])

Enum() 函数创建枚举类

除了通过继承 Enum 类的方法创建枚举类,还可以使用 Enum() 函数创建枚举类。例如:

from enum import Enum
#创建一个枚举类
Color = Enum("Color",('red','green','blue'))
#调用枚举成员的 3 种方式
print(Color.red)
print(Color['red'])
print(Color(1))
#调取枚举成员中的 value 和 name
print(Color.red.value)
print(Color.red.name)
#遍历枚举类中所有成员的 2 种方式
for color in Color:
    print(color)

Enum() 函数可接受 2 个参数,第一个用于指定枚举类的类名,第二个参数用于指定枚举类中的多个成员。

标签:__,name,self,面向对象,实例,print,方法,Python3
From: https://www.cnblogs.com/littlecamel/p/18053866

相关文章

  • 8.Python3 模块和包
    Python3模块和包为此Python提供了一个办法,把这些定义存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py。模块可以被别的程序引入,以使用该模块中的函数等功能。这也是使用python标准库的方法......
  • C# 面向对象
    .NETFramework是一个平台,它类似于Jave的虚拟机,.NET程序是运行在.NETFramwork之上的。.NET框架结构核心包括:.NET基础类库(BaseClassLibrary)和通用语言开发环境(CommonLanguageRuntime).NET基础库由“命名空间(Namespace)”和“类(Class)”组成。4.C#语言的特点:语法简洁(C#源自......
  • JAVA面向对象基础:封装,实体JavaBean
     封装: 封装设计对象规范:合理隐藏,合理暴露 即类中使用的public与private关键字合理使用(只暴露对对象数据操作的方法,不暴露数据,故在对象中用private来封装数据,用public来封装方法)将成员变量保护起来,将get与set方法暴露出来供相关操作。将需要外界访问的成员方法用public,不......
  • JAVA面向对象基础:this关键字
    this;this就是一个变量,可以用在方法中,来拿到拿到当前对象   this的应用场景:this主要用来解决变量名称冲突问题的。   this真正解析: ......
  • python3.6.8 安装解决ssl问题
    https://www.cnblogs.com/mqxs/p/9103031.html#!/bin/bashecho"正在安装相关组件"yuminstall-yopenssl-develbzip2-develexpat-develgdbm-develreadline-develsqlite-develgcc-c++gccopenssl-develecho"下载安装包"wgethttps://www.python.org/ft......
  • JAVA面向对象-第二弹
    Java中,所有的类,都默认直接或者间接继承object封装◆该露的露,该藏的藏◆我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用。封装(数据的隐藏)◆通常,应禁止直接访问一个对象中数据的实际表示,而应......
  • JAVA面向对象基础:入门,搞懂对象
     packagecom.itheima.duyixiang;importjava.util.ArrayList;importjava.util.List;publicclassTest{publicstaticvoidmain(String[]args){Students1=newStudent();s1.name="凯文";s1.yuwen=22;s1.shuxu......
  • centos7 安装python3.8
    #cd/usr/local#yum-ygroupinstall"Developmenttools"#yum-yinstallzlib-develbzip2-developenssl-develncurses-develsqlite-develreadline-develtk-develgdbm-develdb4-devellibpcap-develxz-devel#yuminstalllibffi-devel-ywgethttps:/......
  • python——面向对象——知识汇总三
    Python封装机制及实现方法 封装(Encapsulation),即在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以“类对象.属性名”(或者“类对象.方法名(参数)”)的形式调用这些属性(或方法),而只能用未隐藏的类方法间接操作这些隐藏的属性和方法。    Pyt......
  • python面向对象(Object Oriented Program)
    面向对象(ObjectOrientedProgram)在Python中,所有的数据(包括数字和字符串)实际都是对象,同一类型的对象都有相同的类型。我们可以使用type()函数来获取关于对象的类型信息。什么是类,什么是对象?在Python中,使用类来定义同一种类型的对象。类(class)是广义的数据类型,能够定义复......