How to get the name of a value in an enum declaration? - python

In Python3.7.7, I would like to use the name of an Enum value in the declaration of another value of the same Enum.
My first try:
from enum import Enum
class test(Enum):
value1 = "a"
value2 = value1.name + "b"
This returns an AttributeError: 'str' object has no attribute 'name'. I tried using self.value1 and test.value1 instead and both return a NameError.
I could just use value2 = "value1b" but I fear that it would give me weird errors down the line if I ever change the name of value1 and forget some places instead of an immediate error because of an undefined variable.

During Enum class creation the values are just values -- they are not converted to Enum members until the end of the class definition.
There is nothing currently in the class definition machinery to support your use-case; however, you can use the functional syntax to generate your Enum:
# some code to calculate your names and values (not shown)
# that results in
members = (('value1', 'a'),('value2', 'value1a'))
test = Enum('test', members)
Which results in:
>>> list(test)
[<test.value1: 'a'>, <test.value2: 'value1a'>]

What you try to achieve is not simple, because you can only directly reference from a variable to the value, not vice versa.
I think the easiest solution is using Python3.8+ and using the debugging feature of f-strings (emphasis mine):
To display both the expression text and its value after evaluation, (useful in debugging), an equal sign '=' may be added after the expression.
So the code would turn into:
from enum import Enum
class test(Enum):
value1 = "a"
value2 = f"{value1=}".split("=")[0] + "b"
Other solutions, also working with Python3.7- can be found here. But they are more complicated to implement.

Related

python thousands separator formatting with different character without using locale

I am looking to format thousands with an apostrophe (but could be something else) without using locale or a replace method.
The comma that is used by default in the string formatting must be defined somewhere in the Python source code but I can't find where. Is it possible to access it and change it once so that all formatting in the same session uses the new character?
To be clear (edit after #Matiss comment), I do not want to change the formatting syntax. It should stay as f"{value:,}". However, instead of inserting commas, I would like to insert something else.
Hopefully the comma is declared as a constant somewhere in Python source code (for example string._THOUSANDS_SEPARATOR), and just re-assigning it to soemthing else will do the trick.
I can not find that constant definition.
For example, one of the ways to do it now is:
>>> val = 123456
>>> print(f"{val:,}".replace(",", "'"))
123'456
The replace method is cumbersome and has to be repeated every time.
I also want to avoid the locale environment.
If the comma , is defined as a constant somewhere in Python source code, say for example in a module called python_formatting_constants, and the name of the constant was for example THOUSANDS_SEPARATOR, one could do:
>>> from python_formatting_constants import THOUSANDS_SEPARATOR
>>> THOUSANDS_SEPARATOR = "'"
>>> print(f"{val:,}") # the "," in the formatting string stays the same
123'456
>>> # but the formatted string uses the apostrophe instead of the comma
If you want to do the changes from the core of python, then you might need help from python language developers. But You can create your own format specs by creating your own class and override any provided format specs does not work for you.
class MyInt(int):
def __new__(cls, number):
return super(MyInt, cls).__new__(cls, number)
def __init__(self, value):
self.value = value
def __format__(self, format_spec):
if format_spec == ",":
return format(self.value, ",").replace(",", "'")
return format(self.value, format_spec)
Here I have created my own class which is a subclass of int and I have overloaded the __format__ built-in method of int according to your need.
Example:
# Input
test_value = TestClass(1000)
"{:,}".format(test_value) # alternative: f"{test_value:,}"
# Output
"1'000"
It is just a dummy example, You can do more deep dive into format_spec argument of __format__ method to have your own and replace any existing identifier with your own.
My suggestion will be, it will be quite easier to create your own format_spec than waiting to get some help from core python development team.
Few caveats:
If you are planning to do some add, subtract ... etc with integer and your new class, then you also have to overload all those functions for example __add__, __sub__ because any numeric operation will return pure int but not MyInt which has its own formatting.
Example:
# input
another_value = test_value + 2500
"{:,}".format(another_value)
# output
'3,500'
As you can see, the return value of + is a int but not MyInt so it has used , in the output instead. So you have to do all numeric operation beforehand and then wrap the final numeric result with the MyInt for just to have proper formatting.
More details about the format specs here.

What's the type of a class object in python?

For example, in the given code snippet:
class Test:
variable: string
value1 = Test()
value2 = Test
the value1 has the type Test but what is the type of value2?
I know in other languages there is a type to signify a class object, but given that python type hinting is recent I can't seem to find information on it online.
Try by checking by:
print(type(value1))
print(type(value2))

Python dot-syntax-functions based on condition

Is it possible in python to call dot-syntax-function based on condition. Simple example to turn:
if condition:
foo().bar().baz()
lots_of_code()
else:
foo().baz()
lots_of_code()
def lots_of_code():
# lots of code
into:
foo().(if condition: bar()).baz()
# lots of code only once
No, it is not possible.
The syntax for attribute reference is
attributeref ::= primary "." identifier
Quoting the documentation,
An attribute reference is a primary followed by a period and a name
name must be a regular Python identifier and identifiers can't contain special characters like (.
However, you can use a simple conditional expression to select primary:
(foo().bar() if condition else foo()).baz()
It's equivalent to
if condition:
primary = foo().bar()
else:
primary = foo()
primary.baz()
Note that in this case we have to use parentheses, because attribute reference has higher precedence than conditional expression.
Since foo() is called in either case, start by doing so unconditionally. Save that object to f, with the intention of calling f.baz(). Before that, though, check your condition to see if f should really be the result of foo().bar().
f = foo()
if condition:
f = f.bar()
f.baz()

Getting value of a functions attribute from a string

I am trying to log with syslog. I have a string with the facility in it. In this case "LOG_LOCAL0". I want to use this string to access syslog.LOG_LOCAL0, which is an int of value 128. If this was a function I would use getattr() but I do not know how do do it for something that is not callable like an int, how do I?
Basically I want to call syslog.LOG_LOCAL0 but have the "LOG_LOCAL0" as a string so I can't.
If you're trying to set a value you want setattr(object, name, value) (note there's no underscore btw). So you could do:
setattr(syslog, "LOG_LOCAL0", "The log value")
If you want to use the value then getattr is still appropriate
getattr(syslog, "LOG_LOCAL0") # syslog.LOG_LOCAL0 for all intents and purposes
You can use __dict__ it returns a dictionary of all functions and variables in the class , and then use the variable you want to get as key, to get its value.
Example -
>>> class CA:
... i = 15
... def hello(self):
... print("Hello")
...
>>> CA.__dict__
mappingproxy({'__dict__': <attribute '__dict__' of 'CA' objects>, '
>>> CA.__dict__['i']
15
In your case maybe -
syslog.__dict__['LOG_LOCAL0']
Though a footnote from here -
Except for one thing. Module objects have a secret read-only attribute called dict which returns the dictionary used to implement the module’s namespace; the name dict is an attribute but not a global name. Obviously, using this violates the abstraction of namespace implementation, and should be restricted to things like post-mortem debuggers.

How do I tell what type of data is inside python variable?

I have a list of variables.. inside the list are strings, numbers, and class objects. I need to perform logic based on each different type of data. I am having trouble detecting class objects and branching my logic at that point.
if(type(lists[listname][0]).__name__ == 'str'): # <--- this works for strings
elif(type(lists[listname][0]).__name__ == 'object'): <--- this does not work for classes
in the second line of code above, the name variable contains "Address" as the class name. I was hoping it would contain "class" or "object" so I could branch my program. I will have many different types of objects in the future, so it's a bit impractical to perform logic on every different class name, "Address" "Person" etc
please let me know if my question needs clarification.
thanks!!
FYI: it also makes a difference if its a new-style class or not:
# python
type(1).__name__
'int'
type('1').__name__
'str'
class foo(object):
pass
type(foo()).__name__
'foo'
class bar:
pass
type(bar()).__name__
'instance'
If you can make sure they're all new-style classes, your method will determine the real type. If you make them old-style, it'll show up as 'instance'. Not that I'm recommending making everything all old-style just for this.
However, you can take it one step further:
type(bar().__class__).__name__
'classobj'
type(foo().__class__).__name__
'type'
And always look for 'classobj' or 'type'. (Or the name of the metaclass, if it has one.)
I think you want the isinstance function.
if isinstance(o, ClassName):
However, you'll need to first verify that o is an object, you can use type for that.
It's common in Python to use exception handling to decide which code path to take; inspecting the exact type of an object (with isinstance()) to decide what to do with it is discouraged.
For example, say that what you want to do is, if it's a string, print it in "title case", and if it's an object, you want to call a particular method on it. So:
try:
# is it an object with a particular method?
lists[listname][0].particularMethod()
except AttributeError:
# no, it doesn't have particularMethod(),
# so we expect it to be a string; print it in title case
print lists[listname][0].title()
If you are only interested in handling two types specifically, you could test for them explicitly using isinstance and then handle the leftovers:
import numbers
for item in list:
if isinstance(item, basestring): # (str, unicode)
do_string_thing(item)
elif isinstance(item, numbers.Real): # (int, float, long)
do_number_thing(item)
else:
do_object_thing(item)

Categories

Resources