Reputation: 1307
I have a class Foo:
class Foo(object):
def __init__(self, coord_tpl, coord_list, coord_dict, blah):
# a single coord tuple
# e.g. ((1, 2), (11, 22))
self.coord_tpl = coord_tpl
# a list of coord tuple
# e.g. [((1, 2), (11, 22)), ((3, 4), (33, 44))]
self.coord_list = coord_list
# a dict, whose value is a list of coord tuple
# e.g. { 0: [((1, 2), (11, 22)), ((3, 4), (33, 44))],
# 1: [((5, 6), (55, 66)), ((7, 8), (77, 88))] }
self.coord_dict = coord_dict
# some non-coord attributes
self.blah = blah
I also have an axis translation function: coord_translate
:
def coord_translate(old_coord):
# apply some translation logic to old_coord (x_o, y_o)
new_coord = ...
return new_coord
Question
I have a Foo instance foo:
foo = Foo(...)
How can I translate the coordinate attributes(foo.coord_tpl
, foo.coord_list
and foo.coord_dict
), while with other attributes(foo.blah
) left intact, using coord_translate
, in a more elegant way, instead of recursively handcrafting the translation logic?
UPDATE There does exist recursive structure in the form of attributes: fist simple tuple of coord, then list of tuple of coord, finally dict of list of tuple of coord.
To do the translation logic, I can reflect on the type of each attribute recursively, and apply coord_translate
:
for k, v in foo.__dict__.items():
if isinstance(v, tuple):
setattr(foo, k, (coord_translate(v[0]), coord_translate(v[1])))
elif isinstance(v, list):
for c in v:
...
elif isinstance(v, dict):
...
I want to know if there some other tactics to do this, instead of using clumsy code like if isinstace(...)...elif isinstace(...)... else
?
Upvotes: 1
Views: 204
Reputation: 66449
I personally avoid type-switching and "attribute magic" (I've been bitten by too many bugs and stared at incomprehensible indirections for too long) and prefer to keep everything as explicit and straightforward as possible.
I would define a few helper functions:
def coord_translate(old_coord):
# Dummy implementation
x,y = old_coord
return (x+1, y+1)
def translate_tuple(tpl):
return tuple(map(coord_translate, tpl))
def translate_list(ls):
return list(map(translate_tuple, ls))
def translate_dict(dct):
return dict([(k, translate_list(v)) for (k,v) in dct.items()])
and then write
def translate(self):
self.coord_tpl = translate_tuple(self.coord_tpl)
self.coord_list = translate_list(self.coord_list)
self.coord_dict = translate_dict(self.coord_dict)
You can of course wrap those helper functions in a type-switching function, if you want to.
Upvotes: 1
Reputation: 373
In many other languages like c++ you could overload the function (Define 3 functions named coord_translate but every funtion accepts a different data type) Because python is a strongly typed dynamic language i dont know of any way to do this.
You could use try except instead but as far as i know its
a) bad style
b) not better than your current solution
def coord_translate(self, input):
try:
coord_translate_dict(input)
except ValueError:
coord_translate_list(input)
In my opintion the cleanest solution would be to use a single data type and convert every input to match this type - i´m pretty sure there are already libraries to handle this.
Another approach would be something like this:
def coord_translate_dict(self, dict):
# apply some translation logic
try:
# do your stuff
return True
except:
return False
def coord_translate_list(self, list):
# apply some translation logic
try:
# do your stuff
return True
except:
return False
def coord_translate_tuple(self, tuple):
# apply some translation logic
try:
# do your stuff
return True
except:
return False
def coord_translate(self, input):
if not coord_translate_dict(input) and not coord_translate_list(input):
coord_translate_tuple(input)
Upvotes: 1
Reputation: 17322
you can try:
from abc import ABC, abstractmethod
from collections import defaultdict
class CoordType(ABC):
registry_by_instance = defaultdict(list)
@abstractmethod
def coord_translate(self):
pass
@classmethod
def apply_coord_translate(cls, instance):
for attr in cls.registry_by_instance(instance):
attr.coord_translate()
class CoordTpl(CoordType):
def __init__(self, creation_instance, coord_tpl):
self.coord_tpl = coord_tpl
CoordType.registry_by_instance[creation_instance].append(self)
def coord_translate(self):
# your code to translate
class CoordList(CoordType):
def __init__(self, creation_instance, coord_list):
self.coord_list = coord_list
CoordType.registry_by_instance[creation_instance].append(self)
def coord_translate(self):
# your code to translate
class CoordDict(CoordType):
def __init__(self, creation_instance, coord_dict):
self.coord_dict = coord_dict
CoordType.registry_by_instance[creation_instance].append(self)
def coord_translate(self):
# your code to translate
class Foo(object):
def __init__(self, coord_tpl, coord_list, coord_dict, blah):
# a single coord tuple
# e.g. ((1, 2), (11, 22))
self.coord_tpl = CoordTpl(self.coord_tpl)
# a list of coord tuple
# e.g. [((1, 2), (11, 22)), ((3, 4), (33, 44))]
self.coord_list = CoordList(self, coord_list)
# a dict, whose value is a list of coord tuple
# e.g. { 0: [((1, 2), (11, 22)), ((3, 4), (33, 44))],
1: [((5, 6), (55, 66)), ((7, 8), (77, 88))] }
self.coord_dict = CoordDict(self, coord_dict)
# some non-coord attributes
self.blah = blah
def coord_translate(self):
CoordType.coord_translate(self)
Upvotes: 2