How can I make an Enum that allows reused keys? [duplicate] - python-3.x

I'm trying to get the name of a enum given one of its multiple values:
class DType(Enum):
float32 = ["f", 8]
double64 = ["d", 9]
when I try to get one value giving the name it works:
print DType["float32"].value[1] # prints 8
print DType["float32"].value[0] # prints f
but when I try to get the name out of a given value only errors will come:
print DataType(8).name
print DataType("f").name
raise ValueError("%s is not a valid %s" % (value, cls.name))
ValueError: 8 is not a valid DataType
ValueError: f is not a valid DataType
Is there a way to make this? Or am I using the wrong data structure?

The easiest way is to use the aenum library1, which would look like this:
from aenum import MultiValueEnum
class DType(MultiValueEnum):
float32 = "f", 8
double64 = "d", 9
and in use:
>>> DType("f")
<DType.float32: 'f'>
>>> DType(9)
<DType.double64: 'd'>
As you can see, the first value listed is the canonical value, and shows up in the repr().
If you want all the possible values to show up, or need to use the stdlib Enum (Python 3.4+), then the answer found here is the basis of what you want (and will also work with aenum):
class DType(Enum):
float32 = "f", 8
double64 = "d", 9
def __new__(cls, *values):
obj = object.__new__(cls)
# first value is canonical value
obj._value_ = values[0]
for other_value in values[1:]:
cls._value2member_map_[other_value] = obj
obj._all_values = values
return obj
def __repr__(self):
return '<%s.%s: %s>' % (
self.__class__.__name__,
self._name_,
', '.join([repr(v) for v in self._all_values]),
)
and in use:
>>> DType("f")
<DType.float32: 'f', 8>
>>> Dtype(9)
<DType.float32: 'd', 9>
1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

Related

Printing attributes outside of a class getattr Python

I am trying to print out result1.key and result2.key by using the getattr. However I cannot get the last print function to work. I get the Error: NameError: name 'key' is not defined. How would I be able to make the print functions print(getattr(result1, key)) print(getattr(result2, key)) work to get the Expected Output?
results = ["result1", "result2"]
valuelist1 = [3, 5]
valuelist2 = [10, 6]
def func(val, val2):
return val* val2 + val2/2
class Dummy:
def __init__(self, val, val2):
self.key = func(val, val2)
for counter, runs in enumerate(results):
dummy = Dummy(valuelist1[counter], valuelist2[counter])
globals()[runs] = dummy
print(result1.key)
print(result2.key)
for name in results:
print(getattr(name, "key"))
Expected output:
35.0
33.0
35.0
33.0
You assign the Dummy instance to the globals(). So you need to access them in the same way, because name is only the string/name of your variable (in the global scope).
Do
for name in results:
print(globals()[name].key)
or
for name in results:
print(getattr(globals()[name], "key"))
or (but in general I wouldn't recommend to use eval(), only mentioned as another possible way for the sake of completeness)
for name in results:
print(eval(name).key)

A simple Python program to study classes

For the sake of studying the concept of classes in Python, I have written a program which is meant to calculate the average of a tuple of numbers. However, the program returns an error message which is quoted.
#!/usr/bin/python3
"""
Python program to calculate the average value of
a set of integers or float numbers.
Input format: a tuple, e.g. (1,2,3)
When run, the program generates an error message in line 27
"""
class Mean_value():
def __init__(self, operand):
self.operand = operand
def calculate_average(self, operand):
self.operand = operand
all_in_all = sum(operand)
nmbr = len(operand)
average = all_in_all/nmbr
self.average = average
return self.average
operand = input("Key in numbers as a tuple: ")
print(operand) #temp, the operand is taken in by the program
x = Mean_value.calculate_average(operand) #line 27
print(x)
The error message:
Traceback (most recent call last):
File "D:\Python\Exercise76a.py", line 27, in <module>
x = Mean_value.calculate_average(operand)
TypeError: calculate_average() missing 1 required positional argument: 'operand'
I would highly appreciate any hints from members more experienced than myself.
Any method in your class with self as the first parameter is an instance method, meaning it's supposed to be called on an instance of the class and not on the class itself.
In other words, the self parameter isn't just for show. When you do this:
x = Mean_value(operand)
x.calculate_average(operand)
the python interpreter actually takes x and passes it through to the function as the first parameter (i.e. self). Hence, when you try to call calculate_average() on the class Mean_value instead of on an object of that type, it only passes one of the two required parameters (there's no instance to pass automatically as self, so it just passes the one argument you've given it, leaving the second argument unspecified).
If you want to have a method be static (called on the class instead of on an instance of the class), you should use the #staticmethod decorator on the method in question, and omit the self parameter.
Another way to fix this error is to make your calculate_average method static. Like this:
#staticmethod
def calculate_average(operand):
# but be careful here as you can't access properties with self here.
all_in_all = sum(operand)
nmbr = len(operand)
average = all_in_all/nmbr
return average
The program contains comments
#!/usr/bin/python3
"""
The program computes the average value of a sequence of positive integers,
keyed in as a tuple.
After entering the tuple, the input function returns a string,
e.g.(1,2,3) (tuple) --> (1,2,3) (string).
On screen the two objects look the same.
The major code block deals with reversing the type of the input,
i.e. string --> tuple,
e.g. (1,2,3) (string) --> (1,2,3) (tuple).
The maths is dealt with by the class Average.
"""
class Average:
def __init__(self, tup):
self.tup = tup
def calculate(self):
return sum(self.tup)/len(self.tup)
"""Major code block begins ----------"""
#create containers
L_orig_lst = []
S_str = ""
print("The program computes the average value of")
print("a sequence of positive integers, input as a tuple.\n")
#in orig_str store the string-type of the tuple keyed in
orig_str = input("Key in the numbers as a tuple:\n")
lnth = len(orig_str)
#copy each character from orig_str into the list L_orig_lst (the original list)
for i in range(lnth):
if orig_str[i] in ['(', '.', ',' , ')', '1', '2', '3','4', '5', '6', '7', '8', '9', '0']:
#if one of the characters left parenthesis, period, comma, right parenthesis or a digit
#is found in orig_str, then S_str is extended with that character
S_str = S_str + orig_str[i]
L_orig_lst.append(S_str)
#set S_str to be empty
S_str = ""
elif orig_str[i] == " ":
pass
else:
print("Error in input")
break
#at this stage the following transformation has taken place,
#tuple (string) --> tuple (list)
#e.g. (1,2,3) (string) --> ['(' , '1' , ',' , '2' , ',' , '3' , ')'], L_orig_lst
#create new container
#and set S_str to be empty
L_rev_lst = []
S_str = ""
lnth = len(L_orig_lst)
#from the original list, L_orig_lst, copy those elements which are digits (type string)
#and append them to the revised list, L_rev_lst.
for i in range(lnth):
if L_orig_lst[i] in ['1', '2', '3','4', '5', '6', '7', '8', '9', '0']:
#if the element in the original list is a digit (type string),
#then extend S_str with this element
S_str = S_str + L_orig_lst[i]
elif L_orig_lst[i] == ',':
#if the element in the original list is a comma, then finalize the reading of the number,
#convert the current number (type string) into an integer
S_int = int(S_str)
#and append the integer to the revised list
L_rev_lst.append(S_int)
#set S_str to be empty
S_str = ""
else:
pass
#also convert the last number (type string) to an integer,
S_int = int(S_str)
#and also append the last integer to the revised list
L_rev_lst.append(S_int)
#now convert the revised list into a tuple
T_tpl = tuple(L_rev_lst)
"""Major code block ends --------"""
#calculate the average for the tuple T_tpl
#call the class
x = Average(T_tpl)
#instantiate the class
y = x.calculate()
print("Average value: ", y)

Write a program to take dictionary from the keyboard and print sum of values?

d =dict(input('Enter a dictionary'))
sum = 0
for i in d.values():
sum +=i
print(sum)
outputs: Enter a dictionary{'a': 100, 'b':200, 'c':300}
this is the problem arises:
Traceback (most recent call last):
File "G:/DurgaSoftPython/smath.py", line 2, in <module>
d =dict(input('Enter a dictionary'))
ValueError: dictionary update sequence element #0 has length 1; 2 is required
You can't create a dict from a string using the dict constructor, but you can use ast.literal_eval:
from ast import literal_eval
d = literal_eval(input('Enter a dictionary'))
s = 0 # don't name your variable `sum` (which is a built-in Python function
# you could've used to solve this problem)
for i in d.values():
s +=i
print(s)
Output:
Enter a dictionary{'a': 100, 'b':200, 'c':300}
600
Using sum:
d = literal_eval(input('Enter a dictionary'))
s = sum(d.values())
print(s)
import json
inp = input('Enter a dictionary')
inp = dict(json.loads(inp))
sum = sum(inp.values())
print(sum)
input Enter a dictionary{"a": 100, "b":200, "c":300}
output 600
Actually the return of input function is a string. So, in order to have a valid python dict you need to evaluate the input string and convert it into dict.
One way to do this can be done using literal_eval from ast package.
Here is an example:
from ast import literal_eval as le
d = le(input('Enter a dictionary: '))
_sum = 0
for i in d.values():
_sum +=i
print(_sum)
Demo:
Enter a dictionary: {'a': 100, 'b':200, 'c':300}
600
PS: Another way can be done using eval but it's not recommended.

Can't add variable to an instance of List

I'd like to add a variable called foo to the weights of a Conv2D layer to keep track of some book keeping.
This is my attempt:
kernels = model.get_layer(name='conv2d_1').get_weights()
kernels.foobar = 4
Note that kernels is of type list.
However, the previous code produce the following error:
AttributeError: 'list' object has no attribute 'foobar'
Any idea?
You can't use kernels.__setattr__('foobar', 4) / setattr(kernels, 'foobar', 4) in the way that you want, so you can't set an arbitrary attribute like you can with a custom class.
Perhaps in this case you do a very basic subclass on list:
class Kernels(list):
def __setattr__(self, attr, x):
# Make sure you aren't overriding some method of lists,
# such as `.append()`
if attr in self.__dir__():
raise ValueError('%s is already a method.')
super().__setattr__(attr, x)
>>> k = Kernels([1, 2, 3])
>>> k.foobar = 4
>>> k
[1, 2, 3]
>>> k.foobar
4

accessing a static dict in Python 3.6

I have an Enum class of compass directions as follows.
I also have an 'opposites' dict declared in the same class.
from enum import Enum
class Compass(Enum):
N = 'N' # North
S = 'S' # South
E = 'E' # East
W = 'W' # West
opposites = {N: S, S: N, E: W, W: E}
# static method to provide the opposite values.
#staticmethod
def other(com):
return opposites[com]
when I attempt to call other, eg. Compass.other(Compass.N), I expect to get Compass.S, but instead I am getting..
TypeError: 'Com' object is not subscriptable
What's going on, and how can I remedy this pythonically ?
The basic problem is that opposite is being transformed into an Enum member just like N, S,E, and W are. The next problem is the values in opposite -- they do not get transformed into Enum members.
Ideally, we would have something like:
# NB: does not currently work!
class Compass(Enum):
N = 'N', S
S = 'S', N
E = 'E', W
W = 'W', E
Compass.E.opposite is Compass.W # True
The reasons this does not currently work are twofold:
the final transformation from plain value to Enum member happens after the class has been created
forward references are not allowed
So, to get a clean(er) implementation and API we have to post-process the Enum. I would use a decorator:
class reverse():
"decorator to add reverse lookups to Enum members"
def __init__(self, reverse_map):
"initialize decorator by saving map"
self.reverse_map = reverse_map
def __call__(self, enum):
"apply map to newly created Enum"
for first, second in self.reverse_map.items():
enum[first].opposite = enum[second]
enum[second].opposite = enum[first]
# don't forget to return the now-decorated Enum
return enum
and in use:
#reverse({'N':'S', 'E':'W'})
class Compass(Enum):
N = 'N' # North
S = 'S' # South
E = 'E' # East
W = 'W' # West
>>> Compass.N.opposite is Compass.S
True
Your custom class Compass is derived from Enum class which is enumeration but not subscriptable sequence.
Consider this line:
print(type(Compass.N))
While you expect it to output <class 'str'> - it outputs:
<enum 'Compass'>
To access enumaration object property use value attribute.
print(Compass.N.value) # prints "N"
print(Compass.opposites.value) # prints {'S': 'N', 'N': 'S', 'E': 'W', 'W': 'E'}
A proper Compass.other() function declaration should look as below:
# static method to provide the opposite values.
#staticmethod
def other(item):
if item in Compass.opposites.value:
return Compass.opposites.value[item]
else:
raise AttributeError('unknown compass direction :', item)
Usage:
print(Compass.other(Compass.N.value)) # prints "S"
#RomanPerekhrest got the credit for this purely due to speed of response, but it took a bit more wrangling to get what I wanted, which was an enum from the class. The cast to the Enum itself raises an error if bad input is put into it..
The class file folloeinh RomanPerekhrest that worked for me looks like this.
from enum import Enum
class Compass(Enum):
N = 'N' # North
S = 'S' # South
E = 'E' # East
W = 'W' # West
_opposites = {N: S, S: N, E: W, W: E}
#staticmethod
def other(item):
return Compass(Compass._opposites.value[item.value])
if __name__ == "__main__":
print(Compass.other(Compass.E))
However, #EthanFurman's response is beautiful, and I actually implemented that, not that I completely understand it yet...

Resources