Your first order of business should be to create a new data-type to represent the allowable dictionary keys.
Let us suppose that the new data-type is named Country
from enum import Enum, auto
class Country(Enum):
def _generate_next_value_(name, start, count, last_values):
return name
RUSSIA = auto()
CHINA = auto()
UNITED_STATES = auto()
AUSTRALIA = auto()
BRAZIL = auto()
Let us inspect some examples:
example1 = Country("UNITED_STATES")
example2 = Country("RUSSIA")
print("str(example1)".ljust(20), " == ", str(example1))
print("repr(example1)".ljust(20), " == ", repr(example1))
print("example2".ljust(20), " == ", example2)
print("example1.value".ljust(20), " == ", example1.value)
print("example1.name".ljust(20), " == ", example1.name)
The output printed to the console is as follows:
str(example1) == Country.UNITED_STATES
repr(example1) == <Country.UNITED_STATES: 'UNITED_STATES'>
example2 == Country.RUSSIA
example1.value == UNITED_STATES
example1.name == UNITED_STATES
Normally, enumerated data-types are whole numbers.
For example RUSSIA == 41.
However, it is more self-documenting to use strings.
Feel free to use something other than a enumerated data type (enum). The goal is simply to create a class to represent allowable dictionary keys.
Below is an implementation which is not an enumerated data-type:
import string
class Kountry:
BRAZIL = "BRAZIL"
CHINA = "CHINA"
UNITED_STATES = "UNITED_STATES"
def __init__(this, dirty_stryng:str):
stryng = "".join(str(ch) for ch in dirty_stryng)
stryng = stryng.upper()
is_letter = lambda ch: ch in string.ascii_uppercase
stryng = "".join(filter(is_letter, stryng))
if not stryng in type(this).COUNTRIES:
# print invalid input
# maximum 30 characters. do not print 9,000 characters
# remove line-breaks, carriage returns etc...
# string representation of invalid input must fit all on one line
msg = repr(str(dirty_stryng))[:30]
raise ValueError(msg)
this._string = stryng
def __getattr__(this, attrname:str):
if has_attr(this._string, attrname):
return getattr(this._string, attrname)
return getattr(type(this), attrname)
def __str__(this):
return this._string
We can take this non-enum out for a spin:
brazil = Kountry("brazil")
r = str(brazil)
print("str(brazil)".ljust(20), " is ", r)
r = brazil.split()
print("brazil.split()".ljust(20), " is ", r)
r = brazil.join("$-@")
print("brazil.join(\"$-@\")".ljust(20), " is ", r)
The console output is:
str(brazil) is BRAZIL
brazil.split() is ['BRAZIL']
brazil.join("$-@") is $BRAZIL-BRAZIL@
After defining a new data-type named "Country", you then want to do the following:
Define functions whose input parameters must be instances of the Country class.
Create a dictionary whose keys must be instances of the Country class.
We can create a function whose parameters are supposed to be instances of the Country class as follows:
def to_pretty_string(cntry:Country):
country_str = str(cntry.name)
out_str = "-xXx-".join(["", country_str, ""])
return out_str
Where I wrote :Country on the input parameter... that is a python "type hint". The type-hint says that the function argument should be an instance of the Country class.
In case you are curious, we have the following output to the console when we call (use) the to_pretty_string function:
print(to_pretty_string(Country.BRAZIL))
print(to_pretty_string(Country.UNITED_STATES))
print(to_pretty_string(Country.RUSSIA))
# -xXx-BRAZIL-xXx-
# -xXx-UNITED_STATES-xXx-
# -xXx-RUSSIA-xXx-
In a language like C++, we would use a "template class" for the dictionary. In Java, they are called "generics" instead of "templates". However, it is the same concept whether it be called a "template class" or a "generic class"
I wish that the following was real code:
# WARNING: this is not real python
# Create a dictionary whose keys are Countries
head_counts = Dict[Country]
# Populate the dictionary
head_counts[Country.UNITED_STATES] = 329.5*1000000
head_counts[Country.RUSSIA] = 144.1*1000000
head_counts[Country.BRAZIL] = 212.6*1000000
# End of wishful thinking
However, the python programming language does not have much in the way of generic classes in its standard libraries. You might have to write your own class in order to have a dictionary with string-only keys (Dict[str]) or a dictionary whose keys are decimal numbers only (Dict[float])
What we can do is create a type-hint.
However, the type-hint does not create a real dictionary; only a special object used in parameter lists to specify that the object should probably be a dictionary whose keys are Countries:
import typing as typ
import Country
def funky_the_function(dee:typ.Dict[Country, object]):
print("Keys to `dee` should be instances of `Country`")
dee[Country.UNITED_STATES] = 329.5*1000000
dee[Country.RUSSIA] = 144.1*1000000
dee[Country.BRAZIL] = 212.6*1000000
dee[342] # Danger Will Robinson! Danger!
return