10、枚举与闭包

枚举

枚举类:
from enum import Enum  #导入枚举类

class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4
 
print(VIP.YELLOW)

结果:VIP.YELLOW    #关注的是它的标签而不是数字
枚举和普通类相比有什么优势
三种其他的普通方法表示枚举:
1)
yellow = 1
green = 2
2)
{'yellow':1,'green':2}    
3)
class TypeDiamond():
    yellow = 1
    green = 2

特点:他们都是可变的,可以在代码中轻易的更改值,且没有防止相同标签的功能。
枚举的特点:
from enum import Enum

class VIP(Enum):
    YELLOW = 1
    YELLOW= 2   #不可重复,否则报错
    BLACK = 3
    RED = 4

VIP.YELLOW = 6    #枚举类内的数据不可更改,否则会报错
枚举类型、枚举名称与枚举值
获取枚举变量的数值:
from enum import Enum

class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4
   
print(VIP.YELLOW.value)  #获取变量的数值
结果:1

print(VIP.YELLOW.name)  #获取变量的名称
结果:YELLOW   #str类型

print(VIP.YELLOW)   #获取枚举变量 
print(VIP['YELLOW'])
结果:VIP.YELLOW  #<enum 'VIP'>

print(type(VIP.YELLOW))  #获取获取变量类型
结果:<enum 'VIP'>


枚举是可以遍历的:
for v in VIP :
    print(v)
结果:
VIP.YELLOW
VIP.GREEN
VIP.BLACK
VIP.RED

枚举的比较运算

  • 两个枚举之间可以使用等值比较(==),但不能进行大小比较。
  • 支持身份验证(is操作):result = VIP.BLACK is VIP.GREEN
两个枚举类的变量之间也可以进行等值比较,不过结果是False:
from enum import Enum

class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

class VIP1(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

print(VIP.GREEN ==VIP1.GREEN)
结果:False

枚举注意事项

枚举的数值可以相同,在这种情况下,将第二种枚举类型看成别名。遍历时不会打印别名:
class VIP(Enum):
    YELLOW = 1
    GREEN = 1    #别名,不会报错
    BLACK = 3
    RED = 4

#把别名加入遍历循环:
for v in VIP.__members__.items() :  #获取枚举类成员的具体信息
    print(v)
结果:
('YELLOW', <VIP.YELLOW: 1>)
('GREEN', <VIP.GREEN: 1>)
('BLACK', <VIP.BLACK: 3>)
('RED', <VIP.RED: 4>)


遍历__members__:
for v in VIP.__members__:   #获取枚举类的成员
    print(v)
结果:
YELLOW
GREEN
BLACK
RED

枚举转换

在数据库里一般存储数值或者标签名字来代表枚举类型,推荐存储数值.数字占用的空间更小。但是不建议在代码中用数值代表枚举,可读性不强。

如何将数字转换成枚举类型:
from enum import Enum

a = 1
class VIP(Enum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

print(VIP(a))    #转换枚举类型

结果:VIP.YELLOW

数字枚举

要求每个枚举类型都是数字的时候继承IntEnum:
from enum import IntEnum

class VIP(IntEnum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

限制不同的枚举类型不能取相同的值:
from enum import IntEnum,unique

@unique    #装饰器
class VIP(IntEnum):
    YELLOW = 1
    GREEN = 2
    BLACK = 3
    RED = 4

枚举类型不能实例化,属于单例模式

进阶内容

业务逻辑的开发者,不要考虑太多的封装性
包和类库的开发者,要考虑封装性

一切皆对象

python中的函数是对象,一切皆对象。可以把函数赋值给变量:

a = 1
a = '2'
a = def

甚至可以把函数当作另外一个函数的参数传递或者当成返回值返回。

闭包

内部函数
def curve_pre():
    def curve():
        pass

curve()    #报错,因为curve()的作用域仅限于curve_pre()的内部


闭包
def curve_pre():
    a = 25 
    def curve(x): 
        return a * x * x  #局部变量找不到会到上一级找
    return curve #返回一个函数

f = curve_pre()
print(f(2)) #调用curve()函数
结果:100
外部变量不会影响到闭包
def curve_pre():
    a = 25   #局部变量在curve的外部
    def curve(x):   #接受抛物线的x值
        return a * x * x
    return curve   #返回一个函数

a = 10     #定义a = 10
f = curve_pre()
print(f(2)) #调用curve()函数

结果:100        #仍然是a = 25的取值,取得是定义时的环境变量,这就是闭包
  • 函数及其外部环境变量所构成的整体叫做闭包
  • 环境变量要在函数外部,但不能是全局变量。
错误的闭包示范(使用全局变量)
a = 25    #a定义为了全局变量
def curve_pre():
    def curve(x): #接受抛物线的x值
        return a * x * x
    return curve #返回一个函数

a = 10
f = curve_pre()
print(f(2)) #调用curve()函数

结果:40    #a的值被改变了
查看闭包:
f = curve_pre()
print(f.__closure__)  #闭包函数,如果不是闭包会报错
print(f.__closure__[0].cell_contents)

结果:
(<cell at 0x0031AAF0: int object at 0x0FF93A80>,)  #闭包的存储地址
25    #获取环境变量 a

闭包理解

个人理解:闭包就是一个封闭的环境,这里我们使用函数实现,当外部调用函数。外侧函数(变量+内函数),需要返回内函数。此时才可以调用这个闭包。
闭包函数和普通函数最大的区别就是返回值不是数值。

内函数,没有使用环境变量,会出错。
def f1():
    a = 10
    def f2():
        a = 20    #a被认为是一个局部变量了,就不认为是个环境变量了
        return a
    return f2
 
f = f1()
print(f.__closure__)    #没有__closure__属性
结果:None

正确的闭包示范:
def f1():
    a = 10
    def f2():
        return a
    return f2

f = f1()
print(f.__closure__)
结果:(<cell at 0x02F5AAF0: int object at 0x0FF93990>,)

环境变量不能当作一个变量去赋值,而是一定要去引用外函数的变量(这里与我们定义类时,十分相似)。

旅行者问题

x = 0 为起点,每次计算出旅行者当前所处的位置。

使用全局变量实现:
origin = 0

def go(step):
    global origin    #将origin变成全局变量
    new_pos = origin + step
    origin = new_pos  #如果不提前声明,这一步就会被当作命名新的局部变量,而上一步提前调用了局部变量,从而报错。
    return origin

print(go(2))
print(go(3))
print(go(6))
结果:
2
5
11

使用闭包实现:
origin = 0
def factory(pos):    #工厂模式
    def go(step):
        nonlocal pos #强制声明不是局部变量
        new_pos = pos + step
        pos = new_pos
        return new_pos
    return go

tourist = factory(origin)
print(tourist(2))
print(tourist(3))
print(tourist(6))
结果:
2
5
11
此时并没有改变全局变量origin的值
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,904评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,581评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,527评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,463评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,546评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,572评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,582评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,330评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,776评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,087评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,257评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,923评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,571评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,192评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,436评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,145评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352

推荐阅读更多精彩内容