I'm searching for an elegant way to replace setter/getter methodes handling complex data types by properties using the #property decorator.
The class I'm working on should represent some kind of (network) dimmer. It is possible to request/send "resources" addressed by a specific ID to control the device. I'd like to represent those "resources" as properties of my class hiding the request/send mechanism and the cryptical ID numbers.
Some of those "resources" are just primitive types (int, float, ...) but some are more complex, so I've just created simple classes for them.
This works fine, but there is an ugly source of error: It is not possible to change an attribute of that property directly, I have to set the property completely everytime.
DUMMY_DB = {0x0001: bytearray([0x00])}
class State:
def __init__(self, on, value):
self.on = on
self.value = value
#staticmethod
def from_int(val):
return State(bool(val & 0x80), val & 0x7f)
def __int__(self):
return self.on << 7 | self.value
class Dimmer:
#property
def state(self) -> State:
return State.from_int(self._request(0x0001)[0]) # ID 0x0001 => State
#state.setter
def state(self, val: State):
self._send(0x0001, [int(val)]) # ID 0x0001 => State
# several more properties...
def _request(self, ident) -> bytearray:
# usually get resource 'ident' from network/file/...
return DUMMY_DB[ident]
def _send(self, ident, value):
# usually set resource 'ident' on network/file/... using value
DUMMY_DB[ident] = value
if __name__ == '__main__':
dimmer = Dimmer()
print(dimmer.state.on, dimmer.state.value) # start state
dimmer.state.on = True
dimmer.state.value = 15
print(dimmer.state.on, dimmer.state.value) # state did not change
dimmer.state = State(True, 15)
print(dimmer.state.on, dimmer.state.value) # state changed
The first print is just to show the start state ("False 0"). But the second print shows that
dimmer.state.on = True
dimmer.state.value = 15
are useless. This is because dimmer.state returns a new mutable object which is modified and destroyed without further usage. Only through the complete property assignment the setter methode is called and Dimmer._send invoked.
I think this might be extremely unintuitive and error-prone. Do you have any suggestions for a better design?
I'm wondering how could one create a program to detect the following cases in the code, when comparing a variable to hardcoded values, instead of using enumeration, dynamically?
class AccountType:
BBAN = '000'
IBAN = '001'
UBAN = '002'
LBAN = '003'
I would like the code to report (drop a warning into the log) in the following case:
payee_account_type = self.get_payee_account_type(rc) # '001' for ex.
if payee_account_type in ('001', '002'): # Report on unsafe lookup
print 'okay, but not sure about the codes, man'
To encourage people to use the following approach:
payee_account_type = self.get_payee_account_type(rc)
if payee_account_type in (AccountType.IBAN, AccountType.UBAN):
print 'do this for sure'
Which is much safer.
It's not a problem to verify the == and != checks like below:
if payee_account_type == '001':
print 'codes again'
By wrapping payee_account_type into a class, with the following __eq__ implemented:
class Variant:
def __init__(self, value):
self._value = value
def get_value(self):
return self._value
class AccountType:
BBAN = Variant('000')
IBAN = Variant('001')
UBAN = Variant('002')
LBAN = Variant('003')
class AccountTypeWrapper(object):
def __init__(self, account_type):
self._account_type = account_type
def __eq__(self, other):
if isinstance(other, Variant):
# Safe usage
return self._account_type == other.get_value()
# The value is hardcoded
log.warning('Unsafe comparison. Use proper enumeration object')
return self._account_type == other
But what to do with tuple lookups?
I know, I could create a convention method wrapping the lookup, where the check can be done:
if IbanUtils.account_type_in(account_type, AccountType.IBAN, AccountType.UBAN):
pass
class IbanUtils(object):
def account_type_in(self, account_type, *types_to_check):
for type in types_to_check:
if not isinstance(type, Variant):
log.warning('Unsafe usage')
return account_type in types_to_check
But it's not an option for me, because I have a lot of legacy code I cannot touch, but still need to report on.
I am defining models for my app and I need to a column named 'status' for various verification procedures. Here is a simplified user model.
class User
id(int)
name(str)
status(int) # 0- New 1-Active 2-Inactive 3-Reported 4-Deleted
I asked a fellow Python developer to review my code; and he suggested that I avoided 'magic numbers'. His solution is this:
class Choices:
#classmethod
def get_value(cls, key):
# get the string display if need to show
for k, v in cls.CHOICES:
if k == key:
return v
return ""
class UserStatusChoices(Choices):
NEW = 0
ACTIVE = 1
INACTIVE = 2
REPORTED = 3
DELETED = 4
CHOICES = (
(NEW, "NEW"),
(ACTIVE, "ACTIVE"),
(INACTIVE, "INACTIVE"),
(REPORTED, "REPORTED"),
(DELETED, "DELETED"),
)
Couldn't I use simple dictionaries instead? Does anyone see a good reason for 'class'y solution?
Building on Python Enum class (with tostring fromstring)
class Enum(object):
#classmethod
def tostring(cls, val):
for k,v in vars(cls).iteritems():
if v==val:
return k
#classmethod
def fromstring(cls, str):
return getattr(cls, str.upper(), None)
#classmethod
def build(cls, str):
for val, name in enumerate(str.split()):
setattr(cls, name, val)
class MyEnum(Enum):
VAL1, VAL2, VAL3 = range(3)
class YourEnum(Enum):
CAR, BOAT, TRUCK = range(3)
class MoreEnum(Enum):
pass
print MyEnum.fromstring('Val1')
print MyEnum.tostring(2)
print MyEnum.VAL1
print YourEnum.BOAT
print YourEnum.fromstring('TRUCK')
# Dodgy semantics for creating enums.
# Should really be
# MoreEnum = Enum.build("CIRCLE SQUARE")
MoreEnum.build("CIRCLE SQUARE")
print MoreEnum.CIRCLE
print MoreEnum.tostring(1)
print MoreEnum.tostring(MoreEnum.CIRCLE)
EDIT Added build class method so that a string could be used to build the enums.
Although there are probably better solutions out there.
I've got a bad smell in my code. Perhaps I just need to let it air out for a bit, but right now it's bugging me.
I need to create three different input files to run three Radiative Transfer Modeling (RTM) applications, so that I can compare their outputs. This process will be repeated for thousands of sets of inputs, so I'm automating it with a python script.
I'd like to store the input parameters as a generic python object that I can pass to three other functions, who will each translate that general object into the specific parameters needed to run the RTM software they are responsible. I think this makes sense, but feel free to criticize my approach.
There are many possible input parameters for each piece of RTM software. Many of them over-lap. Most of them are kept at sensible defaults, but should be easily changed.
I started with a simple dict
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar_azimuth_angle: 73, #degrees
solar_zenith_angle: 17, #degrees
...
}
There are a lot of parameters, and they can be cleanly categorized into groups, so I thought of using dicts within the dict:
config = {
day_of_year: 138,
time_of_day: 36000, #seconds
solar: {
azimuth_angle: 73, #degrees
zenith_angle: 17, #degrees
...
},
...
}
I like that. But there are a lot of redundant properties. The solar azimuth and zenith angles, for example, can be found if the other is known, so why hard-code both? So I started looking into python's builtin property. That lets me do nifty things with the data if I store it as object attributes:
class Configuration(object):
day_of_year = 138,
time_of_day = 36000, #seconds
solar_azimuth_angle = 73, #degrees
#property
def solar_zenith_angle(self):
return 90 - self.solar_azimuth_angle
...
config = Configuration()
But now I've lost the structure I had from the second dict example.
Note that some of the properties are less trivial than my solar_zenith_angle example, and might require access to other attributes outside of the group of attributes it is a part of. For example I can calculate solar_azimuth_angle if I know the day of year, time of day, latitude, and longitude.
What I'm looking for:
A simple way to store configuration data whose values can all be accessed in a uniform way, are nicely structured, and may exist either as attributes (real values) or properties (calculated from other attributes).
A possibility that is kind of boring:
Store everything in the dict of dicts I outlined earlier, and having other functions run over the object and calculate the calculatable values? This doesn't sound fun. Or clean. To me it sounds messy and frustrating.
An ugly one that works:
After a long time trying different strategies and mostly getting no where, I came up with one possible solution that seems to work:
My classes: (smells a bit func-y, er, funky. def-initely.)
class SubConfig(object):
"""
Store logical groupings of object attributes and properties.
The parent object must be passed to the constructor so that we can still
access the parent object's other attributes and properties. Useful if we
want to use them to compute a property in here.
"""
def __init__(self, parent, *args, **kwargs):
super(SubConfig, self).__init__(*args, **kwargs)
self.parent = parent
class Configuration(object):
"""
Some object which holds many attributes and properties.
Related configurations settings are grouped in SubConfig objects.
"""
def __init__(self, *args, **kwargs):
super(Configuration, self).__init__(*args, **kwargs)
self.root_config = 2
class _AConfigGroup(SubConfig):
sub_config = 3
#property
def sub_property(self):
return self.sub_config * self.parent.root_config
self.group = _AConfigGroup(self) # Stinky?!
How I can use them: (works as I would like)
config = Configuration()
# Inspect the state of the attributes and properties.
print("\nInitial configuration state:")
print("config.rootconfig: %s" % config.root_config)
print("config.group.sub_config: %s" % config.group.sub_config)
print("config.group.sub_property: %s (calculated)" % config.group.sub_property)
# Inspect whether the properties compute the correct value after we alter
# some attributes.
config.root_config = 4
config.group.sub_config = 5
print("\nState after modifications:")
print("config.rootconfig: %s" % config.root_config)
print("config.group.sub_config: %s" % config.group.sub_config)
print("config.group.sub_property: %s (calculated)" % config.group.sub_property)
The behavior: (output of execution of all of the above code, as expected)
Initial configuration state:
config.rootconfig: 2
config.group.sub_config: 3
config.group.sub_property: 6 (calculated)
State after modifications:
config.rootconfig: 4
config.group.sub_config: 5
config.group.sub_property: 20 (calculated)
Why I don't like it:
Storing configuration data in class definitions inside of the main object's __init__() doesn't feel elegant. Especially having to instantiate them immediately after definition like that. Ugh. I can deal with that for the parent class, sure, but doing it in a constructor...
Storing the same classes outside the main Configuration object doesn't feel elegant either, since properties in the inner classes may depend on the attributes of Configuration (or their siblings inside it).
I could deal with defining the functions outside of everything, so inside having things like
#property
def solar_zenith_angle(self):
return calculate_zenith(self.solar_azimuth_angle)
but I can't figure out how to do something like
#property
def solar.zenith_angle(self):
return calculate_zenith(self.solar.azimuth_angle)
(when I try to be clever about it I always run into <property object at 0xXXXXX>)
So what is the right way to go about this? Am I missing something basic or taking a very wrong approach? Does anyone know a clever solution?
Help! My python code isn't beautiful! I must be doing something wrong!
Phil,
Your hesitation about func-y config is very familiar to me :)
I suggest you to store your config not as a python file but as a structured data file. I personally prefer YAML because it looks clean, just as you designed in the very beginning. Of course, you will need to provide formulas for the auto calculated properties, but it is not too bad unless you put too much code. Here is my implementation using PyYAML lib.
The config file (config.yml):
day_of_year: 138
time_of_day: 36000 # seconds
solar:
azimuth_angle: 73 # degrees
zenith_angle: !property 90 - self.azimuth_angle
The code:
import yaml
yaml.add_constructor("tag:yaml.org,2002:map", lambda loader, node:
type("Config", (object,), loader.construct_mapping(node))())
yaml.add_constructor("!property", lambda loader, node:
property(eval("lambda self: " + loader.construct_scalar(node))))
config = yaml.load(open("config.yml"))
print "LOADED config.yml"
print "config.day_of_year:", config.day_of_year
print "config.time_of_day:", config.time_of_day
print "config.solar.azimuth_angle:", config.solar.azimuth_angle
print "config.solar.zenith_angle:", config.solar.zenith_angle, "(calculated)"
print
config.solar.azimuth_angle = 65
print "CHANGED config.solar.azimuth_angle = 65"
print "config.solar.zenith_angle:", config.solar.zenith_angle, "(calculated)"
The output:
LOADED config.yml
config.day_of_year: 138
config.time_of_day: 36000
config.solar.azimuth_angle: 73
config.solar.zenith_angle: 17 (calculated)
CHANGED config.solar.azimuth_angle = 65
config.solar.zenith_angle: 25 (calculated)
The config can be of any depth and properties can use any subgroup values. Try this for example:
a: 1
b:
c: 3
d: some text
e: true
f:
g: 7.01
x: !property self.a + self.b.c + self.b.f.g
Assuming you already loaded this config:
>>> config
<__main__.Config object at 0xbd0d50>
>>> config.a
1
>>> config.b
<__main__.Config object at 0xbd3bd0>
>>> config.b.c
3
>>> config.b.d
'some text'
>>> config.b.e
True
>>> config.b.f
<__main__.Config object at 0xbd3c90>
>>> config.b.f.g
7.01
>>> config.x
11.01
>>> config.b.f.g = 1000
>>> config.x
1004
UPDATE
Let us have a property config.b.x which uses both self, parent and subgroup attributes in its formula:
a: 1
b:
x: !property self.parent.a + self.c + self.d.e
c: 3
d:
e: 5
Then we just need to add a reference to parent in subgroups:
import yaml
def construct_config(loader, node):
attrs = loader.construct_mapping(node)
config = type("Config", (object,), attrs)()
for k, v in attrs.iteritems():
if v.__class__.__name__ == "Config":
setattr(v, "parent", config)
return config
yaml.add_constructor("tag:yaml.org,2002:map", construct_config)
yaml.add_constructor("!property", lambda loader, node:
property(eval("lambda self: " + loader.construct_scalar(node))))
config = yaml.load(open("config.yml"))
And let's see how it works:
>>> config.a
1
>>> config.b.c
3
>>> config.b.d.e
5
>>> config.b.parent == config
True
>>> config.b.d.parent == config.b
True
>>> config.b.x
9
>>> config.a = 1000
>>> config.b.x
1008
Well, here's an ugly way to at least make sure your properties get called:
class ConfigGroup(object):
def __init__(self, config):
self.config = config
def __getattribute__(self, name):
v = object.__getattribute__(self, name)
if hasattr(v, '__get__'):
return v.__get__(self, ConfigGroup)
return v
class Config(object):
def __init__(self):
self.a = 10
self.group = ConfigGroup(self)
self.group.a = property(lambda group: group.config.a*2)
Of course, at this point you might as well forego property entirely and just check if the attribute is callable in __getattribute__.
Or you could go all out and have fun with metaclasses:
def config_meta(classname, parents, attrs):
defaults = {}
groups = {}
newattrs = {'defaults':defaults, 'groups':groups}
for name, value in attrs.items():
if name.startswith('__'):
newattrs[name] = value
elif isinstance(value, type):
groups[name] = value
else:
defaults[name] = value
def init(self):
for name, value in defaults.items():
self.__dict__[name] = value
for name, value in groups.items():
group = value()
group.config = self
self.__dict__[name] = group
newattrs['__init__'] = init
return type(classname, parents, newattrs)
class Config2(object):
__metaclass__ = config_meta
a = 10
b = 2
class group(object):
c = 5
#property
def d(self):
return self.c * self.config.a
Use it like this:
>>> c2.a
10
>>> c2.group.d
50
>>> c2.a = 6
>>> c2.group.d
30
Final edit (?): if you don't want to have to "backtrack" using self.config in subgroup property definitions, you can use the following instead:
class group_property(property):
def __get__(self, obj, objtype=None):
return super(group_property, self).__get__(obj.config, objtype)
def __set__(self, obj, value):
super(group_property, self).__set__(obj.config, value)
def __delete__(self, obj):
return super(group_property, self).__del__(obj.config)
class Config2(object):
...
class group(object):
...
#group_property
def e(config):
return config.group.c * config.a
group_property receives the base config object instead of the group object, so paths always start from the root. Therefore, e is equivalent to the previously defined d.
BTW, supporting nested groups is left as an exercise for the reader.
Wow, I just read an article about descriptors on r/python today, but I don't think hacking descriptors is going to give you what you want.
The only thing I know that handles sub-configurations like that is flatland. Here's how it would work in Flatland anyhow.
But you could do:
class Configuration(Form):
day_of_year = Integer
time_of_day = Integer
class solar(Form):
azimuth_angle = Integer
solar_angle = Integer
Then load the dictionary in
config = Configuration({
day_of_year: 138,
time_of_day: 36000, #seconds
solar: {
azimuth_angle: 73, #degrees
zenith_angle: 17, #degrees
...
},
...
})
I love flatland, but I'm not sure you gain much by using it.
You could add a metaclass or decorator to your class definition.
something like
def instantiate(klass):
return klass()
class Configuration(object):
#instantiate
class solar(object):
#property
def azimuth_angle(self):
return self.azimuth_angle
That might be better. Then create a nice __init__ on Configuration that can load all the data from a dictionary. I dunno maybe someone else has a better idea.
Here's something a little more complete (without as much magic as LaC's answer, but slightly less generic).
def instantiate(clazz): return clazz()
#dummy functions for testing
calc_zenith_angle = calc_azimuth_angle = lambda(x): 3
class Solar(object):
def __init__(self):
if getattr(self,'azimuth_angle',None) is None and getattr(self,'zenith_angle',None) is None:
return AttributeError("must have either azimuth_angle or zenith_angle provided")
if getattr(self,'zenith_angle',None) is None:
self.zenith_angle = calc_zenith_angle(self.azimuth_angle)
elif getattr(self,'azimuth_angle',None) is None:
self.azimuth_angle = calc_azimuth_angle(self.zenith_angle)
class Configuration(object):
day_of_year = 138
time_of_day = 3600
#instantiate
class solar(Solar):
azimuth_angle = 73
#zenith_angle = 17 #not defined
#if you don't want auto-calculation to be done automagically
class ConfigurationNoAuto(object):
day_of_year = 138
time_of_day = 3600
#instantiate
class solar(Solar):
azimuth_angle = 73
#property
def zenith_angle(self):
return calc_zenith_angle(self.azimuth_angle)
config = Configuration()
config_no_auto = ConfigurationNoAuto()
>>> config.day_of_year
138
>>> config_no_auto.day_of_year
138
>>> config_no_auto.solar.azimuth_angle
73
>>> config_no_auto.solar.zenith_angle
3
>>> config.solar.zenith_angle
3
>>> config.solar.azimuth_angle
7
I think I would rather subclass dict so that it fell back to a default if no data was available. Something like this:
class fallbackdict(dict):
...
defaults = { 'pi': 3.14 }
x_config = fallbackdict(defaults)
x_config.update({
'planck': 6.62606957e-34
})
The other aspect can be addressed with callables. Wether this is elegant or ugly depends on wether datatype declarations are useful:
pi: (float, 3.14)
calc = lambda v: v[0](v[1])
x_config.update({
'planck': (double, 6.62606957e-34),
'calculated': (lambda x: 1.0 - calc(x_config['planck']), None)
})
Depending on the circumstances, the lambda might be broken out if it is used many times.
Don't know if it is better, but it mostly preserves the dictionary style.
I want to create a special dictionary which uses object IDs as keys, like this:
class ObjectIdDict(dict):
def __setitem__(self, key, value):
super(ObjectIdDict, self).__setitem__(id(key), value)
def __getitem__(self, key):
super(ObjectIdDict, self).__getitem__(id(key))
But if I run the following test, I get an error:
class ObjectIdDictTest(unittest.TestCase):
def test_get_and_set(self):
dict_to_test = ObjectIdDict()
class Something:
def __init__(self):
self.x = 1
s = Something()
dict_to_test[s.x] = "message"
self.assertEqual(dict_to_test[s.x], "message")
Error message:
AssertionError: None != 'message'
What is wrong here?
Background:
The reason for creating such an exotic dict is that I want to store validation errors for each field of an object and want to avoid field names as strings: domain_object.errors[domain_object.field1] otherwise field names as strings (domain_object.errors["field1"]) would be bad for refactoring and code completion.
ΤΖΩΤΖΙΟΥ:
I'm certain you don't get anything by
using IDs. obj.field1= 1;
print(id(obj.field1)); obj.field1= 2;
print(id(obj.field1))
If I would not use IDs, the key would be the value of the variable, not its address. This would lead to errors if two fields had the same value:
def test_ordinary_dict(self):
dict_to_test = {}
class Something:
def __init__(self):
self.x = 1
self.y = 1 # same value as self.x!
s = Something()
dict_to_test[s.x] = "message for x"
dict_to_test[s.y] = "message for y"
self.assertEqual(dict_to_test[s.x], "message for x")
# fails because dict_to_test[s.x] == dict_to_test[1] what results in:
# "message for y"
It is not critical that changing a variables value lead to a new address since the validation result is no longer valid after that.
__getitem__ must return the result:
def __getitem__(self, key):
return super(ObjectIdDict, self).__getitem__(id(key))
#^^^^^
Without a return, the implicit return value is None, and therefore oiddict[key] is None for all keys.