I noticed an oddity in the Python 3 Enums (link).
If you set the value of an Enum to a function, it prevents the attribute from being wrapped as an Enum object, which prevents you from being able to use the cool features like EnumCls['AttrName'] to dynamically load the attribute.
Is this a bug? Done on purpose?
I searched for a while but found no mention of restricted values that you can use in an Enum.
Here is sample code that displays the issue:
class Color(Enum):
Red = lambda: print('In Red')
Blue = lambda: print('In Blue')
print(Color.Red) # <function> - should be Color.Red via Docs
print(Color.Blue) # <function> - should be Color.Bluevia Docs
print(Color['Red']) # throws KeyError - should be Color.Red via Docs
Also, this is my first time asking, so let me know if there's anything I should be doing differently! And thanks for the help!
You can override the __call__ method:
from enum import Enum, auto
class Color(Enum):
red = auto()
blue = auto()
def __call__(self, *args, **kwargs):
return f'<font color={self.name}>{args[0]}</font>'
Can then be used:
>>> Color.red('flowers')
<font color=red>flowers</font>
The documentation says:
The rules for what is allowed are as follows: _sunder_ names (starting and ending with a single underscore) are reserved by enum and cannot be used; all other attributes defined within an enumeration will become members of this enumeration, with the exception of __dunder__ names and descriptors (methods are also descriptors).
A "method" is just a function defined inside a class body. It doesn't matter whether you define it with lambda or def. So your example is the same as:
class Color(Enum):
def Red():
print('In Red')
def Blue():
print('In Blue')
In other words, your purported enum values are actually methods, and so won't become members of the Enum.
If someone need/want to use Enum with functions as values, its possible to do so by using a callable object as a proxy, something like this:
class FunctionProxy:
"""Allow to mask a function as an Object."""
def __init__(self, function):
self.function = function
def __call__(self, *args, **kwargs):
return self.function(*args, **kwargs)
A simple test:
from enum import Enum
class Functions(Enum):
Print_Function = FunctionProxy(lambda *a: print(*a))
Split_Function = FunctionProxy(lambda s, d='.': s.split(d))
Functions.Print_Function.value('Hello World!')
# Hello World!
Functions.Split_Function.value('Hello.World.!')
# ['Hello', 'World', '!']
You can also use functools.partial to trick the enum into not considering your function a method of Color:
from functools import partial
from enum import Enum
class Color(Enum):
Red = partial(lambda: print('In Red'))
Blue = partial(lambda: print('In Blue'))
With this you can access name and value as expected.
Color.Red
Out[17]: <Color.Red: functools.partial(<function Color.<lambda> at 0x7f84ad6303a0>)>
Color.Red.name
Out[18]: 'Red'
Color.Red.value()
In Red
I ran into this issue recently, found this post, and first was tempted to use the wrapper pattern suggested in the other related post. However eventually I found out that this was a bit overkill for what I had to do. In the past years this happened to me several times with Enum, so I would like to share this simple experience feedback:
if you need an enumeration, ask yourself whether you actually need an enum or just a namespace.
The difference is simple: Enum members are instances of their host enum class, while namespace members are completely independent from the class, they are just located inside.
Here is an example of namespace containing callables, with a get method to return any of them by name.
class Foo(object):
""" A simple namespace class with a `get` method to access members """
#classmethod
def get(cls, member_name: str):
"""Get a member by name"""
if not member_name.startswith('__') and member_name != 'get':
try:
return getattr(cls, member_name)
except AttributeError:
pass
raise ValueError("Unknown %r member: %r" % (cls.__name__, member_name))
# -- the "members" --
a = 1
#staticmethod
def welcome(name):
return "greetings, %s!" % name
#staticmethod
def wave(name):
return "(silently waving, %s)" % name
w = Foo.get('welcome')
a = Foo.get('a')
Foo.get('unknown') # ValueError: Unknown 'Foo' member: 'unknown'
See also this post on namespaces.
Initially, I thought your issue was just missing commas because I got the output you were expecting.:
from enum import Enum
class Color(Enum):
Red = lambda: print('In Red'),
Blue = lambda: print('In Blue'),
print(Color.Red)
print(Color.Blue)
print(Color['Red'])
output (python3.7)
$ /usr/local/opt/python/bin/python3.7 ~/test_enum.py
Color.Red
Color.Blue
Color.Red
#BernBarn was kind enough to explain that in my solution that a tuple is being created, and to invoke the function would require dereferencing value[0]. There is already another answer using value[0] in this way. I miss rb for this.
Related
I have an Enum subclass that I would like to do two things:
Use the lowercased name of the enum as the value for each item and a property called function. I don't want to have to type this out each time, so I'm using auto().
Optionally, allow the user to pass a custom function name that overrides the lowercased name.
Here is the code I have come up with to do that:
from enum import Enum, auto
class LowerNameEnum(Enum):
def _generate_next_value_(name, start, count, last_values):
print(f"{name=}")
return name.lower()
class Task(str, LowerNameEnum):
def __new__(cls, value, function=None):
obj = str.__new__(cls, value)
obj._value_ = value
obj.function = function or "generic_function"
return obj
DO_SOMETHING = auto()
DO_SOMETHING_ELSE = auto()
DO_ANOTHER_THING = auto(), "do_it_do_it"
THING_4 = auto()
for t in Task:
print(t)
print(" ", t.value)
print(" ", t.function)
But when I run this, I get the following:
$ python myenum.py
name='DO_SOMETHING'
name='DO_SOMETHING_ELSE'
name='THING_4'
Task.DO_SOMETHING
do_something
generic_function
Task.DO_SOMETHING_ELSE
do_something_else
generic_function
Task.DO_ANOTHER_THING
<enum.auto object at 0x103000160>
do_it_do_it
Task.THING_4
thing_4
generic_function
When I try to use the auto() value in combination with the extra argument, I get what looks like a __repr__ of an enum.auto object instead of the lowercased item name. This is because _generate_next_value_ isn't even called for this one, despite my having passed auto().
What am I doing wrong?
Update: Well, it turns out that value in the __new__ method for the third item is an enum.auto object. So that's how it's getting set as the value. I can use isinstance to capture this, which is good. But then my question becomes: how do I find out the name of the item at this point so I can pass it to _generate_next_value_? Or is there some better way to do this?
Until this is fixed (in 3.12), you'll need to use aenum:
from aenum import Enum
...
class Task(str, LowerNameEnum):
...
DO_ANOTHER_THING = auto(function="do_it_do_it")
Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
I would like to replace an object instance by another instance inside a method like this:
class A:
def method1(self):
self = func(self)
The object is retrieved from a database.
It is unlikely that replacing the 'self' variable will accomplish whatever you're trying to do, that couldn't just be accomplished by storing the result of func(self) in a different variable. 'self' is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.
As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won't change the object type) from an member function. I think this will achieve that:
class A:
def method1(self):
newObj = func(self)
self.__dict__.update(newObj.__dict__)
It is not a direct answer to the question, but in the posts below there's a solution for what amirouche tried to do:
Python object conversion
Can I dynamically convert an instance of one class to another?
And here's working code sample (Python 3.2.5).
class Men:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a men! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_men(self):
print('I made The Matrix')
class Women:
def __init__(self, name):
self.name = name
def who_are_you(self):
print("I'm a women! My name is " + self.name)
def cast_to(self, sex, name):
self.__class__ = sex
self.name = name
def method_unique_to_women(self):
print('I made Cloud Atlas')
men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix
men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas
Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don't know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can't init class like Class(args), only Class() (I'm not a pro and can't answer why it's like this).
Yes, all that will happen is that you won't be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn't recommend it though, it makes for less readable code.
Note that you're only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can't change your instance by setting self.
What func(self) should do is to change the variables of your instance:
def func(obj):
obj.var_a = 123
obj.var_b = 'abc'
Then do this:
class A:
def method1(self):
func(self) # No need to assign self here
In many cases, a good way to achieve what you want is to call __init__ again. For example:
class MyList(list):
def trim(self,n):
self.__init__(self[:-n])
x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]
Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.
Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).
A situation where you could do this is:
some_method(self, ...):
...
if(some_condition):
self = self.some_other_method()
...
return ...
Sure, you could start the method body by reassigning self to some other variable, but if you wouldn't normally do that with other parametres, why do it with self?
One can use the self assignment in a method, to change the class of instance to a derived class.
Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.
class aclass:
def methodA(self):
...
if condition:
self = replace_by_derived(self)
# self is now referencing to an instance of a derived class
# with probably the same values for its data attributes
# all code here remains untouched
...
self.methodB() # calls the methodB of derivedclass is condition is True
...
def methodB(self):
# methodB of class aclass
...
class derivedclass(aclass):
def methodB(self):
#methodB of class derivedclass
...
But apart from such a special use case, I don't see any advantages to replace self.
You can make the instance a singleton element of the class
and mark the methods with #classmethod.
from enum import IntEnum
from collections import namedtuple
class kind(IntEnum):
circle = 1
square = 2
def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]
class Shape(namedtuple('Shape', 'k,l,b,u,r')):
self = None
#classmethod
def __repr__(cls):
return "<Shape({},{},{},{},{}) object at {}>".format(
*(attr(cls.self)+[id(cls.self)]))
#classmethod
def transform(cls, func):
cls.self = cls.self._replace(**func(cls.self))
Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self
def nextkind(self):
return {'k': self.k+1}
print(repr(s)) # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s)) # <Shape(2,2,3,4,5) object at 139766656561888>
I have a simple Enum:
class E(Enum):
A = 'a'
B = 'b'
To access 'a' I must type E.A.value. However, the value is the only thing I need from the Enum object.
How to write an Enum, where 'a' could be accessed just by E.A?
Using an int as value was just an example. It should actually be a user-defined class.
If you mixin a class/type with the Enum, then simply accessing the member itself will give you a subtype of that type:
from enum import Enum
class MyClass:
def __init__(self, color):
self.color = color
class MyEnum(MyClass, Enum):
first = 'red'
second = 'green'
third = 'blue'
and in use:
>>> MyEnum.first
<MyEnum.first: 'red'>
>>> MyEnum.first.color
'red'
>>> type(MyEnum.first)
<enum 'MyEnum'>
>>> isinstance(MyEnum.first, MyClass)
True
Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.
I looked around a lot, and I couldn't find a good solution to this problem using the Enum class you're trying to use. However, if you're willing to eschew the idea of using the Enum as a superclass, you can kludge this together:
class Demo:
# something with a 'value' method in it
def __init__(self, val):
self.value = val
def custom_enum(cls):
# class decorator to get __getattribute__() to work properly
# this is necessary because __getattribute__() only exists as an instance method,
# and there seems to be no direct equivalent for class methods
return cls()
#custom_enum
class E:
# first, define our enumerated variables in a dict
_enums = {
'A': Demo('a'),
'B': Demo('b'),
'chicken': Demo('cluck')
}
# then, override __getattribute__() to first get the key from the dict,
# and return the .value property of it
def __getattribute__(self, key):
# because of the decorator, we can't call self._enums or else we get a RecursionError
# therefore, we need to implicitly subclass `object`, and then
# deliberately invoke object.__getattribute__ on self, to access _enums
my_enums = object.__getattribute__(self, '_enums')
return my_enums[key].value
Actually defining the values of your enumerable is as simple as editing the _enums dict. And once you've done so, it should work roughly as you want it to:
>>> E.A
'a'
>>> E.B
'b'
>>> E.chicken
'cluck'
From here you could modify the implementation however necessary (such as returning an AttributeError instead of a KeyError, for example, or overriding __setattr__() to make enum values non-settable, or whatever).
I want to replace string literals in my code, as I want to minimize risk of typos, especially in dict key sets:
a['typoh'] = 'this is bad'
I don't want to type things in twice (risk of a missed typo on the value)
I want it to be "trackable" by various IDEs (i.e. click thru to see where it is defined and escape completion).
Enums are out: 'E.a.name' to get 'a' is dumb.
I have been told this can be done with slots, but I can't figure out how without a little trickery. I can think of a few ways below:
This is an unacceptable answer:
class TwiceIsNotNice(object):
this_is_a_string = 'this_is_a_string'
... (five thousand string constants in)
this_has_a_hard_to_spot_typographical_error =
'this_has_a_had_to_spot_typographical_error'
... (five thousand more string constants)
A clear but annoying way is with a "Stringspace" class/object where the attributes are set via a string list passed in. This solves the minimized typo risk, is VERY easy to read, but has neither IDE trackability nor autocompletion. It's okay, but makes people complain (please don't complain here, I am simply showing how it could be done):
string_consts = Stringspace('a', 'b',...,'asdfasdfasdf')
print(string_consts.a)
... where:
class Stringspace(object):
def __init__(self, *strlist):
for s in strlist:
setattr(self, s, s)
Another way is to define a class using a sentinel object, setting the value in a post phase. This is okay, is trackable, presents itself as an actual class, allows for aliases, etc. But it requires an annoying extra call at the end of the class:
same = object()
class StrList(object):
this_is_a_strval = same
this_is_another_strval = same
this_gets_aliased = "to something else"
# This would of course could become a function
for attr in dir(StrList):
if getattr(StrList, attr) is same:
setattr(StrList, attr, attr)
print(StrList.a)
If this is what the slot magic is supposedly about, then I am disappointed, as one would have to actually instantiate an object:
class SlotEnum(object):
__slots__ = []
def __init__(self):
for k in self.__slots__:
setattr(self, k, k)
class Foo(SlotEnum):
__slots__ = ['a', 'b']
foo_enum_OBJECT = Foo()
print(foo_enum_OBJECT.a)
Enums are out: E.a.name to get a is dumb.
from enum import Enum, auto
class StrEnum(str, Enum):
"base class for Enum members to be strings matching the member's name"
def __repr__(self):
return '<%s.%s>' % (self.__class__.__name__, self.name)
def __str__(self):
return self.name
class E(StrEnum):
a = auto()
this_is_a_string = auto()
no_typo_here = auto()
>>> print(repr(E.a))
<E.a>
>>> print(E.a)
a
>>> print('the answer is: %s!' % E.a)
the answer is: a!
I found one solution at this external link using a custom meta class, for your class containing the string member variables:
Step 1 of 2: The custom meta class can be defined like this:
class MetaForMyStrConstants(type):
def __new__(metacls, cls, bases, classdict):
object_attrs = set(dir(type(cls, (object,), {})))
simple_enum_cls = super().__new__(metacls, cls, bases, classdict)
simple_enum_cls._member_names_ = set(classdict.keys()) - object_attrs
non_members = set()
for attr in simple_enum_cls._member_names_:
if attr.startswith('_') and attr.endswith('_'):
non_members.add(attr)
else:
setattr(simple_enum_cls, attr, attr)
simple_enum_cls._member_names_.difference_update(non_members)
return simple_enum_cls
Step 2 of 2: The class defining your strings can be defined like this (with dummy values, eg, empty tuples):
class MyStrConstants(metaclass=MetaForMyStrConstants):
ONE_LONG_STR = ()
ANOTHER_LONG_STR = ()
THE_REAL_LONGEST_STR = ()
Testing it out:
print (MyStrConstants.ONE_LONG_STR)
print (MyStrConstants.ANOTHER_LONG_STR)
print (MyStrConstants.THE_REAL_LONGEST_STR)
Output:
ONE_LONG_STR
ANOTHER_LONG_STR
THE_REAL_LONGEST_STR
I want to be able to create objects based on an enumeration class, and use a dictionary. Something like this:
class IngredientType(Enum):
SPAM = auto() # Some spam
BAKE_BEANS = auto() # Baked beans
EGG = auto() # Fried egg
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class BakedBeans(Ingredient):
pass
class Egg(Ingredient):
pass
class IngredientFactory(object):
"""Factory makes ingredients"""
choice = {
IngredientType.SPAM: IngredientFactory.MakeSpam,
IngredientType.BAKED_BEANS: IngredientFactory.MakeBakedBeans,
IngredientType.EGG: IngredientFactory.MakeEgg
}
#staticmethod
def make(type):
method = choice[type]
return method()
#staticmethod
def makeSpam():
return Spam()
#staticmethod
def makeBakedBeans():
return BakedBeans()
#staticmethod
def makeEgg():
return Egg()
But I get the error:
NameError: name 'IngredientFactory' is not defined
For some reason the dictionary can't be created.
Where am I going wrong here?
Python is not Java and doesn't require everything to be in a class. Here your IngredientFactory class has no states and only staticmethods, so it's actually just a singleton namespace, which in python is canonically done using the module as singleton namespace and plain functions. Also since Python classes are already callable, wrapping the instanciation in a function doesn't make sense. The simple, straightforwrad pythonic implementation would be:
# ingredients.py
class IngredientType(Enum):
SPAM = auto() # Some spam
BAKE_BEANS = auto() # Baked beans
EGG = auto() # Fried egg
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class Beans(Ingredient):
pass
class Egg(Ingredient):
pass
_choice = {
IngredientType.SPAM: Spam,
IngredientType.BAKED_BEANS: Beans,
IngredientType.EGG: Egg
}
def make(ingredient_type):
cls = _choice[ingredient_type]
return cls()
And the client code:
import ingredients
egg = ingredients.make(ingredients.IngredientType.EGG)
# or much more simply:
egg = ingredients.Egg()
FWIW the IngredientType enum doesn't bring much here, and even makes things more complicated that they have to be - you could just use plain strings:
# ingredients.py
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class Beans(Ingredient):
pass
class Egg(Ingredient):
pass
_choice = {
"spam": Spam,
"beans": Beans,
"egg": Egg
}
def make(ingredient_type):
cls = _choice[ingredient_type]
return cls()
And the client code:
import ingredients
egg = ingredients.make("egg")
Or if you really want to use an Enum, you can at least get rid of the choices dict by using the classes themselves as values for the enum as suggested by MadPhysicist:
# ingredients.py
class Ingredient(object):
pass
class Spam(Ingredient):
pass
class Beans(Ingredient):
pass
class Egg(Ingredient):
pass
class IngredientType(Enum):
SPAM = Spam
BEANS = Beans
EGG = Egg
#staticmethod
def make(ingredient_type):
return ingredient_type.value()
and the client code
from ingredients import IngredientType
egg = IngredientType.make(IngredientType.EGG)
But I really don't see any benefit here either
EDIT: you mention:
I am trying to implement the factory pattern, with the intent of hiding the creation of objects away. The user of the factory then just handles 'Ingredients' without knowledge of the concrete type
The user still have to specify what kind of ingredients he wants (the ingredient_type argument) so I'm not sure I understand the benefit here. What's your real use case actually ? (the problem with made up / dumbed down examples is that they don't tell the whole story).
After looking at Bruce Eckel's book I came up with this:
#Based on Bruce Eckel's book Python 3 example
# A simple static factory method.
from __future__ import generators
import random
from enum import Enum, auto
class ShapeType(Enum):
CIRCLE = auto() # Some circles
SQUARE = auto() # some squares
class Shape(object):
pass
class Circle(Shape):
def draw(self): print("Circle.draw")
def erase(self): print("Circle.erase")
class Square(Shape):
def draw(self): print("Square.draw")
def erase(self): print("Square.erase")
class ShapeFactory(object):
#staticmethod
def create(type):
#return eval(type + "()") # simple alternative
if type in ShapeFactory.choice:
return ShapeFactory.choice[type]()
assert 0, "Bad shape creation: " + type
choice = { ShapeType.CIRCLE: Circle,
ShapeType.SQUARE: Square
}
# Test factory
# Generate shape name strings:
def shapeNameGen(n):
types = list(ShapeType)
for i in range(n):
yield random.choice(types)
shapes = \
[ ShapeFactory.create(i) for i in shapeNameGen(7)]
for shape in shapes:
shape.draw()
shape.erase()
This gets the user to select a class type from the enumeration, and blocks any other type. It also means user's are less likely to write 'bad strings' with spelling mistakes. They just use the enums.
The output from the test is then, something like this:
Circle.draw
Circle.erase
Circle.draw
Circle.erase
Square.draw
Square.erase
Square.draw
Square.erase
Circle.draw
Circle.erase
Circle.draw
Circle.erase
Square.draw
Square.erase
Place your mapping at the end of the class, and reference the methods directly, since they're in the same namespace:
choice = {
IngredientType.SPAM: makeSpam,
IngredientType.BAKED_BEANS: makeBakedBeans,
IngredientType.EGG: makeEgg
}
A class object is not created until all the code in the class body, so you can't access the class itself. However, since the class body is processed in a dedicated namespace, you can access any attribute you've defined up to that point (which is why the mapping has to come at the end). Note also that while you can access globals and built-ins, you can't access the namespaces of enclosing classes or functions.
Here's the detailed but still introductory explanation from the official docs explaining how classes are executed: https://docs.python.org/3/tutorial/classes.html#a-first-look-at-classes