python专栏:【全网最强】Python系统学习手册
目录
一、类与对象
用于描述复杂的对象,包含很多属性和方法。
类(Class):某一些有共同特征的对象的抽象,是某种概念。
对象(Object):也称为实例(instance),是具体存在的实体,每个实体具备一定的特征。对象之间有相同点也有不同点。
1.面向对象
- 面向对象(Object-Oriented Programming)是一种编程范式,是模块化设计的重要方法。
- 世界由各种对象组成,不同对象之间可以相互作用和通讯。
- 描述对象特征的信息称为属性(Attribute)
- 存取属性的函数称为方法(Method)
- 具有相同属性与方法的对象(Object),属于同一个类别,称之为一个类(Class),其中的一个对象为该类的实例(Instance)
2.动态类型语句
- python是动态类型语言,不止属性值可以变化,对象的所属类别、方法都可以变化。
- 一个对象可以动态转换为不同的类,具有不同的属性和方法。属性和方法可以动态的添加和删除,具备高度的灵活性。
3.类的定义
- class 类名(基类):
零到多个成员变量...
零到多个执行语句...
零到多个成员方法...
- 类名只要是合法的标识符即可,合法的变量名可以作为类名。推荐使用大写字母开头,下划线分隔的一系列有意义的单词作为类名。
- 注意冒号和缩进的规范
- 类定义用class关键字,函数定义用def关键字,类中的方法(成员函数)也用def定义。
- 类中的成员变量和方法的顺序没有影响,各个成员之间可以相互调用。
4.类的成员
成员变量
- 类变量:属于类本身,用于定义该类整体的状态数据。
- 实例变量:属于该类的某一个对象,用于定义某个对象所包含的状态数据。
成员方法
- 类方法:用于定义一个类的整体行为,不随对象变化,不绑定具体对象。
- 实例方法:用于定义该类的某个对象的行为或功能,只对具体对象。在类中定义的方法默认为实例方法。
成员的动态性
- 类变量可以在任何地方增、删、改:在类中为新变量赋值,可以自动增加类变量。用del删除类变量。
- 对实例变量也类似,也可以在任何地方动态增、删、改。对不存在的赋值就是动态增加,对已有的变量del就是动态删除。
5.构造方法
- 定义了一个类,构造方法就是从类定义出发,生成一个实例。构造方法返回该类的对象。
- python的构造方法有特殊的名字:__init__,必须用这个名字不能随便换!注意init前后都是连续的双下划线。
- 构造方法的第一的参数是self,表示被构造的实例对象。一般会在构造方法中为self.varname赋值,将对象装配好。
- 构造方法结束时不需要使用return,它会自动将装配好的对象也就是self当作返回值。
- python规定,每一个类,必须有一个构造方法。如果你没有定义,python会自动生成一个只有参数self的空的构造方法。
6.实例方法
- 在类中定义的方法,python会自动为它们绑定第一个参数,一般这个参数约定俗成的命名为self。这个参数总是指向调用该方法的对象。
- 由于实例方法的第一个参数self自动绑定,传参的时候不需要传它。self指向的对象是不确定的,但对象的类型是确定的。
- python的类和对象,类似于c++的命名空间,因此在调用类和对象的方法时,一定要使用类.和对象.的形式。
- 用对象.方法(参数)调用实例方法,用类.方法(参数)的方式调用类方法。
- 也可以用类.方法(对象,参数)的方式对某一个实例调用方法。这种方式称为未绑定方法,用户需要指定一个实例对象。
# 第一个面向对象程序,定义人类:一个关于人的类
class Person:
''' 定义一个人 '''
def __init__(self,name,age=0):
''' 构造方法,定义与生俱来的两个属性,姓名和年龄 '''
self.name = name
self.age = age
def birthday(self):
''' 定义实例方法,过生日 ''' # 定义一个实例方法
self.age += 1
print('%s%d岁啦,生日快乐!!!' % (self.name, self.age))
def open_mouth(self):
''' 张嘴 '''
print('%s张嘴。' % self.name)
def chew(self):
''' 咀嚼 '''
print('%s嚼一嚼。' % self.name)
def eat(self):
''' 吃饭 ''' # 定义一个实例方法,依赖其他方法
self.open_mouth() # 在类定义中调用其它的方法,使用self.xxx的方式
self.chew()
a = Person('张三') # 使用构造方法,由于第一个self参数自动绑定,只需传入后面的参数
a.name
a.age
a.birthday() # 调用过生日方法,第一个参数自动绑定,不需要传参了
a.birthday()
a.age
a.eat()
# python中,也可以用 类.方法(对象) 的方式来调用方法,就是看着啰嗦
Person.chew(a)
7.动态增删改
# 实例变量的动态增删改
# 增
a.nickname = '小明'
print(a.nickname)
# 改
a.nickname = '小麻'
print(a.nickname)
# 删
# del a.nickname
print(a.nickname) # 删除之后,这个成员变量不存在了,会报错
# 类变量的动态增删改
# 增
Person.charactor = '勇敢'
print(Person.charactor)
print(a.charactor) # 每个实例都能够调用类变量
# 改
Person.charactor = '智慧'
print(Person.charactor)
print(a.charactor)
# 删
# del Person.charactor
print(Person.charactor)
8.类的继承
- 和其它面向对象语言一样,python也支持类的继承。
- 继承就是:从另一个类中全盘拷贝变量和方法,实现代码复用。被继承的类称为基类,继承者叫派生类。
- 派生类可以在基类的基础上增加自己的变量和方法,基类已经有的不需要再写一遍。对派生类的操作不会影响到基类。
- 继承操作只需要在class 类名后面添加(基类)即可。
class Student(Person):
''' 定义类:学生,继承 “人”这个类别 '''
def __init__(self,name,age=0,school='北师大'):
''' 初始化一个“学生” '''
Person.__init__(self,name,age) # 先使用基类的初始化方法
self.school = school # 在此基础上添加
def gotoschool(self): # 新增一个成员方法
''' 上学函数 '''
print('%s去%s上学啦!' % (self.name, self.school))
b = Student('韩梅梅')
b.gotoschool()
二、方法
1.类方法与静态方法
- python支持定义类方法和静态方法,定义的方式就是使用函数装饰器。
- 使用@classmethod修饰的是类方法。自动绑定类方法的第一个参数cls。
- 使用@staticmethod修饰的是静态方法。静态方法不会自动绑定参数。
- 类方法可以访问类变量,一般用它来对类变量进行增删改。由于python的动态性,这些事情在哪儿都能做,但集中到类方法里面做更方便管理。
- 静态方法其实单独定义的函数没有区别。只有名字和当前类有关。
2.函数装饰器
- 在python的语法中,@符号作为函数装饰器使用,它的作用是引用已有的函数,修饰其它的函数。
- 所谓函数装饰器,相当于给函数“穿马甲”,添加或者改变原有函数的功能。
- python的函数装饰器语法使它能简便优美的实现很多功能,比如类方法,静态方法,以及即时编译(jit)。
class Mosquito:
'''
蚊子类。目前我们只关心蚊子的行为,不关心具体的蚊子,所以没有构造方法。这个类不会产生实例对象。
'''
# 使用@classmethod修饰类方法,自动绑定第一个参数,也就是当前类cls
@classmethod
def fly(cls):
'''
飞行方法,蚊子都会飞。
'''
print('这个类是会飞的: ', cls)
# 使用@staticmethod修饰静态方法
@staticmethod
def sting(p):
'''
叮咬方法,我们关心谁被蚊子叮了,并不关心叮人的具体是哪只蚊子。这个方法的参数是人类对象。
'''
print(p.name + '被蚊子叮了。')
Mosquito.sting(a)
Mosquito.fly()
3.一般的函数装饰器
- 一般的函数装饰器,可以理解为函数套函数。在函数func()的前面加上@gfunc,则产生的函数实际上是gfunc(func())。
- gfunc()必须是一个以函数为参数的函数。
- func()是被装饰得函数,装饰前后,函数的参数不发生变化,返回值可能发生变化,也可能不变。
- 装饰后函数的返回值,事实上就是gfunc()的返回值。
- 函数装饰器作用:用来对一个函数进行包装,在其前后加入通用的辅助功能:
1)在前面,加上权限检查,数据合理性与一致性检查,等必要的前处理。
2)在后面,加上记录日志,保存结果,统计绘图,等必要的后处理。
3)适用于前后处理可以通用,中间的函数算法可以灵活替换的场景。
4)可以使用*args代替不定个数的参数,增加装饰器的通用性。
def pre_post(func):
'''
用装饰器实现通用的前后处理 '''
def pre_post_f(*args):
'''
实际进行前后处理操作的函数 '''
# 用一句话代替前后处理过程
print('前处理......')
f = func(*args)
print('后处理......')
# 内层函数的返回值为func的返回值
return f
# 外层函数,把包装好的内层函数作为返回值
return pre_post_f
# 用通用前后处理函数来包装一个求平均值的函数
@pre_post
def mymean(x):
return sum(x)/len(x)
# 运行mymean函数,实际上运行的是含有前后处理的,用 pre_post包装好了的函数
x = list(range(10))
print(mymean(x))
三、成员变量
- python类的成员变量分为类变量和实例变量,可以动态增删改。
- 类变量和实例变量有不同的用途、定义方式、使用方式。
- 类变量在类的命名空间内定义。
- 实例变量在构造方法内定义。
- 类变量可以通过类名.变量和实例名.变量两种方式访问。
- 实例变量只能通过实例名.变量访问和修改
# 中国城市信息列表
class City:
# 定义类变量
country = 'China' # 中国城市
num_of_cities = 0 # 城市总数
def __init__(self, name, lon, lat, postcode):
self.name = name
self.lon = lon
self.lat = lat
self.postcode = postcode
City.num_of_cities += 1
def print_city(self):
# 使用类变量一定要带上类名,下面这句话会报错
# print('Country: %s' % contry)
# 下面这些是正确的
print('Country: %s' % City.country)
print('Total number of cities: %d' % City.num_of_cities)
print('City name: %s' % self.name)
print('Longitude: %7.3f' % self.lon)
print('Latitude: %7.3f' % self.lat)
print('Post code: %d' % self.postcode)
- 通过实例BJ修改类变量country,然后再从类中访问,发现变量并没有被修改。
- 由于python语言的动态性,用赋值=修改变量的时候,事实上并没有修改类变量country,而是在实例BJ里面新建了一个实例变量,也叫country。
- 因此,修改类变量最好是通过类来修改。通过实例只能读不要增删改!
BJ = City('北京',116.408,39.904,100000)
BJ.print_city()
SH = City('上海',121.445,31.213,200000)
SH.print_city()
GZ = City('广州',113.265,23.108,510000)
GZ.print_city()
# 访问类变量
# 通过类访问
print(City.country)
# 通过类修改
City.country = '中国'
print(City.country)
# 通过实例访问
print(BJ.country)
# 通过实例修改
BJ.country = 'BJ中国' # 通过实例BJ修改
# 从类中访问,发现类变量没有被改过!
print(City.country)
# 从实例BJ中访问,发现被改了
print(BJ.country)
# 从其它实例中访问,发现还是没有被修改
print(SH.country)
# 通过dir发现BJ中新建了一个实例变量,也叫country!
dir(BJ)
四、隐藏和封装
面向对象的三大特征:封装(Encapsulation)、继承和多态。
封装指的是将对象的状态信息隐藏在内部,不允许外部直接访问。通过该类提供的方法,完成数据完整性、一致性、权限检查等操作后才允许访问和修改。
以双下划线__开头的是隐藏变量和方法。
类变量、类方法、实例变量、实例方法,都是可以隐藏的。也可以通过“作弊”的方式访问到隐藏变量和方法。但是非常不推荐这样做!
封装的目的
- 隐藏类的实现细节。限制不合理的访问和修改。
- 通过数据检查保证对象信息的完整性。
- 便于集中修改,提高代码的可维护性。
良好的封装需要从两个方面考虑:
- 该藏的藏起来:隐藏对象的属性和实现细节,不允许外部直接访问。
- 该露的露出来:编写相应的方法操作这些隐藏属性,通过在方法中的各种检查来保证访问和操作的安全。
# 中国城市信息列表,使用封装来增加安全性
class City:
# 定义类变量,所有的类变量都是隐藏变量
# 中国城市
__country = 'China'
# 城市总数
__num_of_cities = 0
def __num_plusone():
''' 隐藏方法,让城市的总数加一。 '''
City.__num_of_cities += 1
def __init__(self, name, lon, lat, postcode):
''' 构造方法,所有的实例变量都是隐藏变量。 '''
self.__name = name
self.__lon = lon
self.__lat = lat
self.__postcode = postcode
City.__num_plusone()
def print_city(self):
''' 把实例对象和类的信息打印出来。 '''
print('Country: %s' % City.__country)
print('Total number of cities: %d' % City.__num_of_cities)
print('City name: %s' % self.__name)
print('Longitude: %7.3f' % self.__lon)
print('Latitude: %7.3f' % self.__lat)
print('Post code: %d' % self.__postcode)
# 读取类和对象的属性
def get_name(self):
return self.__name
def get_lat(self):
return self.__lat
def get_lon(self):
return self.__lon
def get_coordinate(self):
return self.__lat, self.__lon
def get_postcode(self):
return self.__postcode
def get_country():
return City.__country
def get_num_of_cities():
return City.num_of_cities
# 没有写入方法,对象一旦创建就不能修改,类的属性只能通过内部的方法修改
看起来python的隐藏方法是有效的。然而,事实上python并没有真正的隐藏机制,它只是把被隐藏的变量给改了名字。
用_类名__变量或方法名的方式,可以访问到被隐藏的东西。但是平时不建议这样做。
# 创建一些对象
BJ = City('北京',116.408,39.904,100000)
BJ.print_city()
SH = City('上海',121.445,31.213,200000)
SH.print_city()
GZ = City('广州',113.265,23.108,510000)
GZ.print_city()
# 直接访问实例变量和类变量,会报错,隐藏变量从类定义的外部,既不能读也不能写
#print(City.country)
print(BJ.__name)
# 通过get方法间接访问类变量和实例变量
print(City.get_country())
print(BJ.get_name())
# 作弊式访问隐藏变量
print(BJ._City__name)
# 作弊式修改隐藏变量
BJ._City__name = 'Beijing'
print(BJ._City__name)
使用property定义属性
- 使用双下划线隐藏变量和方法,实现封装,然后通过一系列get和set方法实现。
- 可以通过property()函数,把几个变量攒起来,变成虚拟属性。
property()函数的语法格式如下:
property(fget = None, fset = None, fdel = None, doc = None)
- property()函数可以传入四个参数,分别代表读方法(fget)、写方法(fset)、删除方法(fdel)和文档字符串(doc)
- 传入1个参数,表示只读(只有fget)
- 传入2个参数,表示读写(有fget和fset)
- 传入3个参数,表示读写删
- 传入4个参数,表示读、写、删、文档齐全
# 中国城市信息列表,使用property定义属性
class City:
# 定义类变量,所有的类变量都是隐藏变量
# 中国城市
__country = 'China'
# 城市总数
__num_of_cities = 0
def __num_plusone():
''' 隐藏方法,让城市的总数加一。 '''
City.__num_of_cities += 1
def __init__(self, name, lon, lat, postcode):
''' 构造方法,所有的实例变量都是隐藏变量。 '''
self.__name = name
self.__lon = lon
self.__lat = lat
self.__postcode = postcode
City.__num_plusone()
def print_city(self):
''' 把实例对象和类的信息打印出来。 '''
print('Country: %s' % City.__country)
print('Total number of cities: %d' % City.__num_of_cities)
print('City name: %s' % self.__name)
print('Longitude: %7.3f' % self.__lon)
print('Latitude: %7.3f' % self.__lat)
print('Post code: %d' % self.__postcode)
# 读取类和对象的属性
def get_name(self):
return self.__name
def get_lat(self):
return self.__lat
def get_lon(self):
return self.__lon
def get_coordinate(self):
return self.__lat, self.__lon
def get_postcode(self):
return self.__postcode
def get_country():
return City.__country
def get_num_of_cities():
return City.num_of_cities
# 写入对象的属性(类的属性不能直接写)
def set_name(self, name):
self.__name = name
def set_lat(self, lat):
self.__lat = lat
def set_lon(self, lon):
self.__lon = lon
def set_coordinate(self, coordinate):
self.__lat, self.__lon = coordinate
def set_postcode(self, postcode):
self.__postcode = postcode
# 定义coordinate属性的删除操作
def del_coordinate(self):
self.__lat, self.__lon = 0, 0
# 用property定义属性
coordinate = property(get_coordinate, set_coordinate, del_coordinate, \
'城市的经纬度坐标,北纬在前,东经在后,南和西方向为负值')
- property所定义的属性,并不是类定义中真实存在的类或实例变量,而是用已有变量合成出来的。
- 这种属性被称为计算属性,它并不真正的存储什么东西,而是通过类和对象的状态计算出来的。
- 对计算属性赋值,python会自动使用set方法更新相应的类或对象内部变量。
# 创建一些对象
BJ = City('北京',116.408,39.904,100000)
BJ.print_city()
SH = City('上海',121.445,31.213,200000)
SH.print_city()
GZ = City('广州',113.265,23.108,510000)
GZ.print_city()
# 访问coordinate的说明文档
help(City.coordinate)
# 访问coordinate的说明文档,也可以直接取得成员的__doc__字符串
print(City.coordinate.__doc__)
# 访问coordinate属性
print(BJ.coordinate)
# 事实上是通过get方法访问的
# 直接对coordinate属性赋值
BJ.coordinate = 39, 116
print(BJ.coordinate)
# 事实上是通过set方法修改的,是安全的
# 直接访问隐藏的属性,例如lat,还是不行的
print(BJ.__lat)
# 通过get方法访问lat,确认lat也被修改了
print(BJ.get_lat())
五、类的继承
- 继承就是子类全盘拷贝父类的成员(变量和方法),并在此基础上定义自己的成员。
- python支持多继承。但多重继承会令编程复杂度极大增加。最好还是用单继承,然后添加需要的变量和方法。
- 多重继承,指一个子类有多个直接的父类。该子类会得到所有父类的所有方法。
- 如果父类中有同名方法,则前面的父类中的方法会“遮蔽”后面父类的同名方法。
- 看来fly和fire两个函数都在。Plane的构造函数优先,Weapon的构造函数被覆盖了。
# 多重继承例子
# 定义飞机类
class Plane:
def __init__(self, name, speed = 2000):
self.name = name
self.speed = speed # 飞行速度
def fly(self):
print('%s是飞机,可以飞!' % self.name)
# 定义武器类
class Weapon:
def __init__(self, name, attack_p = 500):
self.name = name
self.attack_p = attack_p # 攻击力点数
def fire(self):
print('%s是武器,开火!!!' % self.name)
# 定义战斗机类,继承飞机和武器
class Fighter(Plane, Weapon):
pass # 什么都不做,先看看里面有什么
# 定义战斗机类Fighter的实例
f = Fighter('歼20')
# 看看里面有什么
print(f.name)
print(f.speed)
#print(f.attack_p) # 攻击力属性丢了!
f.fly()
f.fire()
重写父类方法
- 有时候子类不仅仅是需要在父类的基础上增加新的方法,还需要改写父类已有的方法。
- 例如客机、直升机、战斗机都是飞机,但它们的飞行方式不同。直升机能垂直起降,客机都是固定翼飞机不能垂直起降。战斗机一般不能垂直起降,但有一类特殊的飞机(F35B,海鹞,雅克38,雅克141)虽然是固定翼的但也能垂直起降。现代战斗机一般都能超音速飞行。
- 如果子类的同名方法和父类不一样,就需要重写(override)该方法。
# 重写父类方法例子
class Aircraft():
''' 飞行器,包括热气球、飞艇、各种固定翼和旋翼飞机。在大气层内飞行。 '''
def __init__(self, name, maxspeed = 100):
self.name = name
self.maxspeed = maxspeed # 飞行速度
def fly(self):
print('%s是飞行器(aircraft),可以在大气层内飞行。' % self.name)
class Plane(Aircraft):
''' 固定翼飞行器(Fixed-wing aeroplane),一般简称为飞机(plane),利用固定的机翼产生的升力在大气层内飞行。 '''
def fly(self): # 重写父类的方法
print('%s是固定翼飞行器(Fixed-wing aeroplane),利用固定的机翼产生的升力在大气层内飞行。' % self.name)
class Helicopter(Aircraft):
''' 直升飞机(Helicopter),利用旋转的机翼产生的升力在大气层内飞行。 '''
def fly(self): # 重写父类的方法
print('%s直升飞机(Helicopter),利用旋转的机翼产生的升力在大气层内飞行。' % self.name)
# 定义固定翼飞机和直升飞机的实例
a = Plane('C919')
a.fly()
b = Helicopter('直20')
b.fly()
调用被重写的方法
有时候我们需要在子类中调用被重写的方法,只需要指定父类的名字就可以了。这个时候需要显示的给它传入self参数。
# 定义战斗机类,与之前不同的是,这次不使用多重继承,同时测试调用被重写的父类方法
class Fighter(Plane):
def __init__(self, name, maxspeed = 2000, attack_p = 500):
self.name = name
self.maxspeed = maxspeed # 飞行速度
self.attack_p = attack_p # 攻击力点数
# 添加新的方法:开火
def fire(self):
print('%s是武器,开火!!!' % self.name)
# 重写父类方法
def fly(self):
# 首先调用父类的同名方法,注意要给这个方法传入self参数
Plane.fly(self)
# 后面是新添加的内容
print('%s是战斗机,可以高速飞行。' % self.name)
# 定义一个实例,看看fly方法执行的结果如何
c = Fighter('歼20')
c.fly()
使用super函数
- python还支持使用super函数直接调用父类中的方法。我们可以用super函数重写Figher类。
- super函数其实是super类的构造函数,它能自动绑定self,因此不需要传入self参数了。
# 定义战斗机类,使用super函数
class Fighter(Plane):
def __init__(self, name, maxspeed = 2000, attack_p = 500):
# 使用super函数调用父类的构造方法
super().__init__(name, maxspeed)
self.attack_p = attack_p # 攻击力点数
# 添加新的方法:开火
def fire(self):
print('%s是武器,开火!!!' % self.name)
# 重写父类方法
def fly(self):
# 使用super函数调用父类的方法
super().fly()
# 后面是新添加的内容
print('%s是战斗机,可以高速飞行。' % self.name)
# 定义一个实例,看看fly方法执行的结果如何,和上面是一样的
c = Fighter('歼20')
c.fly()
# 看看super函数的帮助,发现它其实是super类的构造方法
help(super)
六、多态
- python属于弱类型、动态类型语言,python的变量本身没有声明类型。变量指向什么类型,它就是什么类型。
- 事实上,多态是一种灵活的编程机制,会根据传入的对象自动执行不同的行为。以飞行器类为例,会根据不同的类型选择不同的方式执行fly方法。
- 多态在编写大型程序的时候非常有用。用户只需给出方法的名称,如何执行完全取决于对象本身。因此能省略大量的if或者switch之类的流程控制。
# 定义实例
a = Plane('C919')
a.fly()
# 同一个变量,换一个类
a = Helicopter('直20')
a.fly()
# 再换一个类,同样是执行fly方法,执行方式不同
a = Fighter('歼20')
a.fly()
类型检查函数
- 多态的使用非常方便,但也有可能引入bug,例如调用不存在的方法,或者方法的行为在意料之外。
- 为了保证程序质量,有时候需要有必要的类型检查,然后再调用方法。
- issubclass(cls,class_or_turple): 检测类的继承关系:检查cls是否为有一个类或者元组中所包含的多个类中任意类的子类。
- isinstance(cls,class_or_turple): 检测类和对象的关系:检查cls是否为有一个类或者元组中所包含的多个类中任意类的子类。
# 用issubclass检测类的继承关系
# Fighter是Plane的子类
print(issubclass(Fighter,Plane))
# Fighter不是Helicopter的子类
print(issubclass(Fighter,Helicopter))
# 使用元组作为第二个参数,只要符合其中一个就是True
print(issubclass(Fighter,(Plane,Helicopter)))
# 用isinstance检测类和对象的关系
a = Fighter('歼20')
# 是Fighter对象吗?
print(isinstance(a,Fighter))
# 是Plane对象吗?
print(isinstance(a,Plane))
# 是Helicopter对象吗?
print(isinstance(a,Helicopter))
# 使用元组作为第二个参数,只要符合其中一个就是True
print(isinstance(a,(Plane,Helicopter)))
七、枚举类
- 枚举类型是一种特殊的数据类型,其中的对象数量是有限个。
- python一般是通过Enum类的构造函数来定义枚举类。enumeration [ɪˌnuːməˈreɪʃn]
- 构造函数Enum()的第一个参数是类名,第二个参数是一个元组,列出所有枚举值。
- 枚举类的每个成员有name和value两个属性。name是成员的名称,value是成员的枚举值(顺序编号,从1开始,记住是从1开始!)
# 定义枚举类,一年四季
import enum
S = enum.Enum('Season',('spring','summer','fall','winter'))
# 访问枚举类
# 打印成员
print(S.spring)
# 打印成员的name属性
print(S.spring.name)
# 打印成员的value属性
print(S.spring.value)
# 通过变量名来访问成员(注意是用方括号,类似于访问字典类型)
print(S['spring'])
# 通过枚举值来访问成员(注意是用圆括号,编号从1开始,不是从0开始!)
print(S(1))
# 枚举类自带的__members__属性,将枚举类转换为字典(有序字典)
print(S.__members__)
枚举类的派生类
- 有时候单纯的枚举类功能太单一了,和只读的有序字典差不多。
- 可以从Enum类派生出我们自己的类,添加我们自己需要的方法。
class Season(enum.Enum):
# 为序列指定value值
spring = '春'
summer = '夏'
fall = '秋'
winter = '冬'
def info(self):
print('这是一个代表季节%s的枚举' % self.value)
# 通过成员名访问
print(Season['spring'])
# 通过枚举值来访问
print(Season('春'))
# 使用info方法
Season['summer'].info()
# 使用__members__遍历有序字典
for name, member in Season.__members__.items():
print(name, ':', member, ',', member.value)