使用__prepare__作为枚举......捕获的是什么?

时间:2017-05-08 18:46:07

标签: python enums metaclass python-3.6

Python enum.Enum的声明性用法需要提供值,当在enum的最基本用例中,我们实际上并不关心名称和值。我们只关心哨兵本身。在最近阅读了related Q&A之后,我意识到可以使用枚举的元类的__prepare__方法来获得这种声明:

class Color(Enum):
    red
    blue
    green

让事情变得如此干燥的实施实际上相当容易:

from collections import defaultdict

class EnumMeta(type):
    @classmethod
    def __prepare__(meta, name, bases):
        return defaultdict(object)

    def __new__(cls, name, bases, classdict):
        classdict.default_factory = None
        return type.__new__(cls, name, bases, classdict)

class Enum(metaclass=EnumMeta):
    pass

在Python 3.6中,提供enum.auto来帮助解决omitting values的问题,但界面仍然很奇怪 - 您需要为auto()指定__repr__值每个成员,并从另一个基础继承,以修复class Color(NoValue): red = auto() blue = auto() green = auto()

auto

知道为标准库选择的实现已经进入了许多工时和非常小心,必须有一些理由为什么早先演示的声明性枚举的更多Pythonic版本无法正常工作。

我的问题是,提出的方法有哪些问题和失败模式,为什么这个(或类似的东西)决定反对 - 而Python中包含def __init__(self, state_size, question_tensor, encoded_questions, batch_size): self._state_size = state_size self.question_tensor = question_tensor self.encoded_questions = encoded_questions self.batch_size = batch_size @property def state_size(self): return self._state_size @property def output_size(self): return self._state_size def __call__(self, inputs, state, scope=None): scope = scope or type(self).__name__ with tf.variable_scope(scope): W_p = tf.get_variable("W_p", dtype=tf.float64, shape=[self.state_size, self.state_size], initializer=tf.contrib.layers.xavier_initializer()) W_r = tf.get_variable("W_r", dtype=tf.float64, shape=[self.state_size, self.state_size], initializer=tf.contrib.layers.xavier_initializer()) b_p = tf.get_variable("b_p", dtype=tf.float64, shape=[self.state_size]) w = tf.get_variable("w", dtype=tf.float64, shape=[1,self.state_size]) b = tf.get_variable("b", dtype=tf.float64, shape=[]) #print 'question tensor', np.shape(self.question_tensor) #print 'inputs', np.shape(inputs) #print 'insides', np.shape(tf.matmul(inputs, W_p) + tf.matmul(state, W_r) + b_p) G = tf.nn.tanh( tf.transpose(tf.transpose(self.question_tensor, perm=[1,0,2]) + (tf.matmul(inputs, W_p) + tf.matmul(state, W_r) + b_p), perm=[1,0,2]) ) #print 'big G', np.shape(G) attention_list = [] for i in range(self.batch_size): attention_matrix = tf.matmul(G[i,:,:], tf.transpose(w)) attention_list.append(attention_matrix) attention_scores = tf.stack(attention_list) a = tf.nn.softmax(attention_scores + b) a = tf.reshape(a, [self.batch_size, -1]) #print 'a shape is', np.shape(a) weighted_question_list = [] for i in range(self.batch_size): attention_vector = tf.matmul(tf.reshape(a[i], [1,-1]), self.encoded_questions[i]) weighted_question_list.append(attention_vector) weighted_questions = tf.stack(weighted_question_list) weighted_questions = tf.reshape(weighted_questions, [32, -1]) #print'weighted questions', np.shape(weighted_questions) z = tf.concat([inputs, weighted_questions], 1) lstm_cell = tf.nn.rnn_cell.LSTMCell(self.state_size) output, new_state = lstm_cell.__call__(z, state) return output, new_state 功能呢?

2 个答案:

答案 0 :(得分:7)

defaultdict成为Enum的命名空间有几个陷阱:

  • 无法访问除其他枚举成员/方法之外的任何内容
  • 拼写错误创建新成员
  • _EnumDict命名空间中丢失保护:
    • 覆盖成员
    • 覆盖方法
    • 较新的_generate方法

最重要的是:

  • 它不会起作用

为什么它会起作用? __prepare__不仅可以在命名空间dict上设置属性,因此命名空间也可以自行命令 - 而_EnumDict可以:_member_names,列出应该是成员的所有属性。

但是,声明一个没有值的名称的目标并非不可能 - aenum 1 包允许它有一些安全措施:

  • 魔术自动行为仅在定义成员时出现(一旦定义了正常方法,它就会关闭)
  • 默认情况下会排除
  • propertyclassmethodstaticmethod,但可以包含它们和/或排除其他全局名称

这种行为对于stdlib来说被认为太神奇了,所以如果你想要它,以及其他一些增强/改进 2 ,你必须使用aenum

一个例子:

from aenum import AutoEnum

class Color(AutoEnum):
    red
    green
    blue

__repr__仍显示创建的值。

-

1 披露:我是Python stdlib Enumenum34 backportAdvanced Enumeration (aenum)图书馆的作者。

2 NamedConstant(就像它说的那样;),NamedTuple(基于元类,默认值等),加上一些内置枚举:

  • MultiValueEnum - >多个值可以映射到一个名称(而不是别名)
  • NoAliasEnum - >具有相同值的名称不是别名(想想扑克牌)
  • OrderedEnum - >成员按定义可比较订单
  • UniqueEnum - >没有别名允许

答案 1 :(得分:1)

您可能感兴趣的是可以使用多个参数创建枚举:

from enum import Enum

class NoValue(Enum):
    def __repr__(self):
        return '<%s.%s>' % (self.__class__.__name__, self.name)

Color = NoValue('Color', ['red', 'green', 'blue'])  # no need for "auto()" calls

这样您就不必使用auto或其他任何内容(例如__prepare__)。

  

为什么这个(或类似的东西)决定反对 - 而自动功能包含在Python 3.6中呢?

已经在Python问题跟踪器(特别是bpo-23591)上对此进行了详细讨论,并且我将包含针对它的(汇总的)参数:

Vedran Čačić

  

这是一个基本的东西:它打破了类主体是一套命令的承诺,其中Python语句(例如赋值)具有它们通常的语义。

Raymond Hettinger

  

只要[auto]已在某处定义(即来自enum import [auto]),它就是普通的Python,并且不与其他语言或其工具链作斗争。

简而言之:类定义将这些“变量”解释为查找:

class A(object):
    a

但是对于enum,它们应该被解释为赋值?该用例根本不被视为"special enough to break the rules"