Is there a way to easily define C-like structures in Python?I am tired of writing down such things:
class MyStruct(): def __init__(self, field1, field2, field3): self.field1 = field1 self.field2 = field2 self.field3 = field3
#1st floor
Whenever I need a Dictionary-like Instant Data Object (I don't think of C structure!)I think of this cute hacker:
class Map(dict): def __init__(self, **kwargs): super(Map, self).__init__(**kwargs) self.__dict__ = self
Now you can say:
struct = Map(field1='foo', field2='bar', field3=42) self.assertEquals('bar', struct.field2) self.assertEquals(42, struct['field3'])
It's convenient when you need a "not a class" package, and when named tuples are hard to understand...
#2nd floor
You can access the C-Style structure in python in the following ways.
class cstruct: var_i = 0 var_f = 0.0 var_str = ""
If you just want to use cstruct objects
obj = cstruct() obj.var_i = 50 obj.var_f = 50.00 obj.var_str = "fifty" print "cstruct: obj i=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)
If you want to create an array of objects for cstruct
obj_array = [cstruct() for i in range(10)] obj_array[0].var_i = 10 obj_array[0].var_f = 10.00 obj_array[0].var_str = "ten" #go ahead and fill rest of array instaces of struct #print all the value for i in range(10): print "cstruct: obj_array i=%d f=%f s=%s" %(obj_array[i].var_i, obj_array[i].var_f, obj_array[i].var_str)
Note: Instead of the'cstruct'name, use the structure name instead of var_i, var_f, var_str. Define the member variables of the structure.
#3rd floor
It may be a bit late, but I've made a solution using Python Meta-Classes (the decorator version below).
When u init_ is called at runtime, it grabs each parameter and its value and assigns them to your class as instance variables.This allows you to create structurally similar classes without having to assign each value manually.
My example has no error checks, so it's easier to follow.
class MyStruct(type): def __call__(cls, *args, **kwargs): names = cls.__init__.func_code.co_varnames[1:] self = type.__call__(cls, *args, **kwargs) for name, value in zip(names, args): setattr(self , name, value) for name, value in kwargs.iteritems(): setattr(self , name, value) return self
This is action.
>>> class MyClass(object): __metaclass__ = MyStruct def __init__(self, a, b, c): pass >>> my_instance = MyClass(1, 2, 3) >>> my_instance.a 1 >>>
I Published on reddit and / u / matchu A cleaner decorative version has been released.I encourage you to use it unless you want to extend the metaclass version.
>>> def init_all_args(fn): @wraps(fn) def wrapped_init(self, *args, **kwargs): names = fn.func_code.co_varnames[1:] for name, value in zip(names, args): setattr(self, name, value) for name, value in kwargs.iteritems(): setattr(self, name, value) return wrapped_init >>> class Test(object): @init_all_args def __init__(self, a, b): pass >>> a = Test(1, 2) >>> a.a 1 >>>
#4th floor
You can subclass the C structures available in the standard libraries. ctypes The module provides a Structure class .Example from document:
>>> from ctypes import * >>> class POINT(Structure): ... _fields_ = [("x", c_int), ... ("y", c_int)] ... >>> point = POINT(10, 20) >>> print point.x, point.y 10 20 >>> point = POINT(y=5) >>> print point.x, point.y 0 5 >>> POINT(1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: too many initializers >>> >>> class RECT(Structure): ... _fields_ = [("upperleft", POINT), ... ("lowerright", POINT)] ... >>> rc = RECT(point) >>> print rc.upperleft.x, rc.upperleft.y 0 5 >>> print rc.lowerright.x, rc.lowerright.y 0 0 >>>
#5th floor
I wrote a decorator that you can use in any way to assign all incoming parameters or any default value to the instance.
def argumentsToAttributes(method): argumentNames = method.func_code.co_varnames[1:] # Generate a dictionary of default values: defaultsDict = {} defaults = method.func_defaults if method.func_defaults else () for i, default in enumerate(defaults, start = len(argumentNames) - len(defaults)): defaultsDict[argumentNames[i]] = default def newMethod(self, *args, **kwargs): # Use the positional arguments. for name, value in zip(argumentNames, args): setattr(self, name, value) # Add the key word arguments. If anything is missing, use the default. for name in argumentNames[len(args):]: setattr(self, name, kwargs.get(name, defaultsDict[name])) # Run whatever else the method needs to do. method(self, *args, **kwargs) return newMethod
Quick demo.Note that I use the location parameter a, the default value of b, and the named parameter c.Then I print all three references to self to indicate that they were assigned correctly before the input method.
class A(object): @argumentsToAttributes def __init__(self, a, b = 'Invisible', c = 'Hello'): print(self.a) print(self.b) print(self.c) A('Why', c = 'Nothing')
Please note that my decorator should use any method, not just u init_u.