dargolith
dargolith

Reputation: 311

Python: Grouped constants with possibility to iterate over group

I have a module with constants (data types and other things).

Let's call the module constants.py

Let's pretend it contains the following:

# noinspection PyClassHasNoInit
class SimpleTypes:
    INT = 'INT'
    BOOL = 'BOOL'
    DOUBLE = 'DOUBLE'

# noinspection PyClassHasNoInit
class ComplexTypes:
    LIST = 'LIST'
    MAP = 'MAP'
    SET = 'SET'

This is pretty and works great, IDEs with inspection will be able to help out by providing suggestions when you code, which is really helpful. PyCharm example below:

IDE Help

But what if I now have a value of some kind and want to check if it is among the ComplexTypes, without defining them in one more place. Would this be possible?

To clarify even more, I want to be able to do something like:

if myVar in constants.ComplexTypeList:
    # do something

Which would of course be possible if I made a list "ComplexTypeList" with all the types in the constants module, but then I would have to add potentially new types to two locations each time (both the class and the list), is it possible to do something dynamically?

I want it to work in python 2.7, though suggestions on how to do this in later versions of python is useful knowledge as well.

SOLUTION COMMENTS:

I used inspect, as Prune suggested in the marked solution below. I made the list I mentioned above within the constants.py module as:

ComplexTypeList = [m[1] for m in inspect.getmembers(ComplexTypes) if not m[0].startswith('_')]

With this it is possible to do the example above.

Upvotes: 5

Views: 2283

Answers (4)

wu1meng2
wu1meng2

Reputation: 547

Since Python 3.4 (PEP 435), you can group constants with an Enum class. An Enum class is iterable and hashable. It disallows member reassignment, which means the enum values are truly constant. See this post for details.

from enum import Enum

class ComplexTypes(Enum):
    LIST = 'LIST'
    MAP = 'MAP'
    SET = 'SET'

# Enum items are iterable
print(f"{list(ComplexTypes)}")

my_type = ComplexTypes.LIST

# Enum items are hashable
print(f"{my_type in ComplexTypes = }")
print(f"{'LIST' in ComplexTypes.__members__ = }")

# Get value of the corresponding enum
val = ComplexTypes.LIST.value
print(f"{val = }")

prints

[<ComplexTypes.LIST: 'LIST'>, <ComplexTypes.MAP: 'MAP'>, <ComplexTypes.SET: 'SET'>]
my_type in ComplexTypes = True
'LIST' in ComplexTypes.__members__ = True
val = 'LIST'

Upvotes: 0

Pouria
Pouria

Reputation: 1091

If I have understood the question correctly, you want to define a custom type. You don't really need to import any modules. There are a number of ways you can do this, e.g. meta classes, however, a simple method is as follows:

my_type = type('ComplexTypes', (object,), {'LIST': 'LIST'})

var = my_type()

Now you can test the type:

type(var)

which returns:

__main__.ComplexTypes

In other words:

type(var) is my_type

which returns True

Upvotes: 1

Prune
Prune

Reputation: 77860

I think I have it. You can use inspect.getmembers to return the items in the module. Each item is a tuple of (name, value). I tried it with the following code. dir gives only the names of the module members; getmembers also returns the values. You can look for the desired value in the second element of each tuple.

constants.py

class ComplexTypes:
    LIST_N = 'LIST'
    MAP_N  = 'MAP'
    SET_N  = 'SET'

test code

from constants import ComplexTypes
from inspect import getmembers, isfunction

print dir(ComplexTypes)

for o in getmembers(ComplexTypes):
    print o

output:

['LIST_N', 'MAP_N', 'SET_N', '__doc__', '__module__']
('LIST_N', 'LIST')
('MAP_N', 'MAP')
('SET_N', 'SET')
('__doc__', None)
('__module__', 'constants')

You can request particular types of objects with the various is operators, such as

getmembers(ComplexTypes, inspect.isfunction)

to get the functions. Yes, you can remove things with such a simple package. I don't see a method to get you constants in a positive fashion. See documentation.

Upvotes: 8

Natecat
Natecat

Reputation: 2182

The builtin method .dir(class) will return all the attributes of a class given. Your if statement can therefore be if myVar in dir(constants.ComplexTypes):

Upvotes: 3

Related Questions