C-like structures in Python - python
Is there a way to conveniently define a C-like structure in Python? I'm tired of writing stuff like:
class MyStruct():
def __init__(self, field1, field2, field3):
self.field1 = field1
self.field2 = field2
self.field3 = field3
Update: Data Classes
With the introduction of Data Classes in Python 3.7 we get very close.
The following example is similar to the NamedTuple example below, but the resulting object is mutable and it allows for default values.
from dataclasses import dataclass
#dataclass
class Point:
x: float
y: float
z: float = 0.0
p = Point(1.5, 2.5)
print(p) # Point(x=1.5, y=2.5, z=0.0)
This plays nicely with the new typing module in case you want to use more specific type annotations.
I've been waiting desperately for this! If you ask me, Data Classes and the new NamedTuple declaration, combined with the typing module are a godsend!
Improved NamedTuple declaration
Since Python 3.6 it became quite simple and beautiful (IMHO), as long as you can live with immutability.
A new way of declaring NamedTuples was introduced, which allows for type annotations as well:
from typing import NamedTuple
class User(NamedTuple):
name: str
class MyStruct(NamedTuple):
foo: str
bar: int
baz: list
qux: User
my_item = MyStruct('foo', 0, ['baz'], User('peter'))
print(my_item) # MyStruct(foo='foo', bar=0, baz=['baz'], qux=User(name='peter'))
Use a named tuple, which was added to the collections module in the standard library in Python 2.6. It's also possible to use Raymond Hettinger's named tuple recipe if you need to support Python 2.4.
It's nice for your basic example, but also covers a bunch of edge cases you might run into later as well. Your fragment above would be written as:
from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")
The newly created type can be used like this:
m = MyStruct("foo", "bar", "baz")
You can also use named arguments:
m = MyStruct(field1="foo", field2="bar", field3="baz")
You can use a tuple for a lot of things where you would use a struct in C (something like x,y coordinates or RGB colors for example).
For everything else you can use dictionary, or a utility class like this one:
>>> class Bunch:
... def __init__(self, **kwds):
... self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)
I think the "definitive" discussion is here, in the published version of the Python Cookbook.
Perhaps you are looking for Structs without constructors:
class Sample:
name = ''
average = 0.0
values = None # list cannot be initialized here!
s1 = Sample()
s1.name = "sample 1"
s1.values = []
s1.values.append(1)
s1.values.append(2)
s1.values.append(3)
s2 = Sample()
s2.name = "sample 2"
s2.values = []
s2.values.append(4)
for v in s1.values: # prints 1,2,3 --> OK.
print v
print "***"
for v in s2.values: # prints 4 --> OK.
print v
How about a dictionary?
Something like this:
myStruct = {'field1': 'some val', 'field2': 'some val'}
Then you can use this to manipulate values:
print myStruct['field1']
myStruct['field2'] = 'some other values'
And the values don't have to be strings. They can be pretty much any other object.
dF: that's pretty cool... I didn't
know that I could access the fields in
a class using dict.
Mark: the situations that I wish I had
this are precisely when I want a tuple
but nothing as "heavy" as a
dictionary.
You can access the fields of a class using a dictionary because the fields of a class, its methods and all its properties are stored internally using dicts (at least in CPython).
...Which leads us to your second comment. Believing that Python dicts are "heavy" is an extremely non-pythonistic concept. And reading such comments kills my Python Zen. That's not good.
You see, when you declare a class you are actually creating a pretty complex wrapper around a dictionary - so, if anything, you are adding more overhead than by using a simple dictionary. An overhead which, by the way, is meaningless in any case. If you are working on performance critical applications, use C or something.
I would also like to add a solution that uses slots:
class Point:
__slots__ = ["x", "y"]
def __init__(self, x, y):
self.x = x
self.y = y
Definitely check the documentation for slots but a quick explanation of slots is that it is python's way of saying: "If you can lock these attributes and only these attributes into the class such that you commit that you will not add any new attributes once the class is instantiated (yes you can add new attributes to a class instance, see example below) then I will do away with the large memory allocation that allows for adding new attributes to a class instance and use just what I need for these slotted attributes".
Example of adding attributes to class instance (thus not using slots):
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(3,5)
p1.z = 8
print(p1.z)
Output: 8
Example of trying to add attributes to class instance where slots was used:
class Point:
__slots__ = ["x", "y"]
def __init__(self, x, y):
self.x = x
self.y = y
p1 = Point(3,5)
p1.z = 8
Output: AttributeError: 'Point' object has no attribute 'z'
This can effectively works as a struct and uses less memory than a class (like a struct would, although I have not researched exactly how much). It is recommended to use slots if you will be creating a large amount of instances of the object and do not need to add attributes. A point object is a good example of this as it is likely that one may instantiate many points to describe a dataset.
You can subclass the C structure that is available in the standard library. The ctypes module provides a Structure class. The example from the docs:
>>> 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
>>>
You can also pass the init parameters to the instance variables by position
# Abstract struct class
class Struct:
def __init__ (self, *argv, **argd):
if len(argd):
# Update by dictionary
self.__dict__.update (argd)
else:
# Update by position
attrs = filter (lambda x: x[0:2] != "__", dir(self))
for n in range(len(argv)):
setattr(self, attrs[n], argv[n])
# Specific class
class Point3dStruct (Struct):
x = 0
y = 0
z = 0
pt1 = Point3dStruct()
pt1.x = 10
print pt1.x
print "-"*10
pt2 = Point3dStruct(5, 6)
print pt2.x, pt2.y
print "-"*10
pt3 = Point3dStruct (x=1, y=2, z=3)
print pt3.x, pt3.y, pt3.z
print "-"*10
Whenever I need an "instant data object that also behaves like a dictionary" (I don't think of C structs!), I think of this cute hack:
class Map(dict):
def __init__(self, **kwargs):
super(Map, self).__init__(**kwargs)
self.__dict__ = self
Now you can just say:
struct = Map(field1='foo', field2='bar', field3=42)
self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])
Perfectly handy for those times when you need a "data bag that's NOT a class", and for when namedtuples are incomprehensible...
Some the answers here are massively elaborate. The simplest option I've found is (from: http://norvig.com/python-iaq.html):
class Struct:
"A structure that can have any fields defined."
def __init__(self, **entries): self.__dict__.update(entries)
Initialising:
>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42
adding more:
>>> options.cat = "dog"
>>> options.cat
dog
edit: Sorry didn't see this example already further down.
You access C-Style struct in python in following way.
class cstruct:
var_i = 0
var_f = 0.0
var_str = ""
if you just want use object of cstruct
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 of 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 'cstruct' name, please use your struct name
instead of var_i, var_f, var_str, please define your structure's member variable.
This might be a bit late but I made a solution using Python Meta-Classes (decorator version below too).
When __init__ is called during run time, it grabs each of the arguments and their value and assigns them as instance variables to your class. This way you can make a struct-like class without having to assign every value manually.
My example has no error checking so it is 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
Here it is in action.
>>> class MyClass(object):
__metaclass__ = MyStruct
def __init__(self, a, b, c):
pass
>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>>
I posted it on reddit and /u/matchu posted a decorator version which is cleaner. I'd encourage you to use it unless you want to expand 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
>>>
I wrote a decorator which you can use on any method to make it so that all of the arguments passed in, or any defaults, are assigned 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
A quick demonstration. Note that I use a positional argument a, use the default value for b, and a named argument c. I then print all 3 referencing self, to show that they've been properly assigned before the method is entered.
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')
Note that my decorator should work with any method, not just __init__.
I don't see this answer here, so I figure I'll add it since I'm leaning Python right now and just discovered it. The Python tutorial (Python 2 in this case) gives the following simple and effective example:
class Employee:
pass
john = Employee() # Create an empty employee record
# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000
That is, an empty class object is created, then instantiated, and the fields are added dynamically.
The up-side to this is its really simple. The downside is it isn't particularly self-documenting (the intended members aren't listed anywhere in the class "definition"), and unset fields can cause problems when accessed. Those two problems can be solved by:
class Employee:
def __init__ (self):
self.name = None # or whatever
self.dept = None
self.salary = None
Now at a glance you can at least see what fields the program will be expecting.
Both are prone to typos, john.slarly = 1000 will succeed. Still, it works.
Here is a solution which uses a class (never instantiated) to hold data. I like that this way involves very little typing and does not require any additional packages etc.
class myStruct:
field1 = "one"
field2 = "2"
You can add more fields later, as needed:
myStruct.field3 = 3
To get the values, the fields are accessed as usual:
>>> myStruct.field1
'one'
Personally, I like this variant too. It extends #dF's answer.
class struct:
def __init__(self, *sequential, **named):
fields = dict(zip(sequential, [None]*len(sequential)), **named)
self.__dict__.update(fields)
def __repr__(self):
return str(self.__dict__)
It supports two modes of initialization (that can be blended):
# Struct with field1, field2, field3 that are initialized to None.
mystruct1 = struct("field1", "field2", "field3")
# Struct with field1, field2, field3 that are initialized according to arguments.
mystruct2 = struct(field1=1, field2=2, field3=3)
Also, it prints nicer:
print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}
There is a python package exactly for this purpose. see cstruct2py
cstruct2py is a pure python library for generate python classes from C code and use them to pack and unpack data. The library can parse C headres (structs, unions, enums, and arrays declarations) and emulate them in python. The generated pythonic classes can parse and pack the data.
For example:
typedef struct {
int x;
int y;
} Point;
after generating pythonic class...
p = Point(x=0x1234, y=0x5678)
p.packed == "\x34\x12\x00\x00\x78\x56\x00\x00"
How to use
First we need to generate the pythonic structs:
import cstruct2py
parser = cstruct2py.c2py.Parser()
parser.parse_file('examples/example.h')
Now we can import all names from the C code:
parser.update_globals(globals())
We can also do that directly:
A = parser.parse_string('struct A { int x; int y;};')
Using types and defines from the C code
a = A()
a.x = 45
print a
buf = a.packed
b = A(buf)
print b
c = A('aaaa11112222', 2)
print c
print repr(c)
The output will be:
{'x':0x2d, 'y':0x0}
{'x':0x2d, 'y':0x0}
{'x':0x31316161, 'y':0x32323131}
A('aa111122', x=0x31316161, y=0x32323131)
Clone
For clone cstruct2py run:
git clone https://github.com/st0ky/cstruct2py.git --recursive
Here is a quick and dirty trick:
>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'
How does it works? It just re-use the builtin class Warning (derived from Exception) and use it as it was you own defined class.
The good points are that you do not need to import or define anything first, that "Warning" is a short name, and that it also makes clear you are doing something dirty which should not be used elsewhere than a small script of yours.
By the way, I tried to find something even simpler like ms = object() but could not (this last exemple is not working). If you have one, I am interested.
NamedTuple is comfortable. but there no one shares the performance and storage.
from typing import NamedTuple
import guppy # pip install guppy
import timeit
class User:
def __init__(self, name: str, uid: int):
self.name = name
self.uid = uid
class UserSlot:
__slots__ = ('name', 'uid')
def __init__(self, name: str, uid: int):
self.name = name
self.uid = uid
class UserTuple(NamedTuple):
# __slots__ = () # AttributeError: Cannot overwrite NamedTuple attribute __slots__
name: str
uid: int
def get_fn(obj, attr_name: str):
def get():
getattr(obj, attr_name)
return get
if 'memory test':
obj = [User('Carson', 1) for _ in range(1000000)] # Cumulative: 189138883
obj_slot = [UserSlot('Carson', 1) for _ in range(1000000)] # 77718299 <-- winner
obj_namedtuple = [UserTuple('Carson', 1) for _ in range(1000000)] # 85718297
print(guppy.hpy().heap()) # Run this function individually.
"""
Index Count % Size % Cumulative % Kind (class / dict of class)
0 1000000 24 112000000 34 112000000 34 dict of __main__.User
1 1000000 24 64000000 19 176000000 53 __main__.UserTuple
2 1000000 24 56000000 17 232000000 70 __main__.User
3 1000000 24 56000000 17 288000000 87 __main__.UserSlot
...
"""
if 'performance test':
obj = User('Carson', 1)
obj_slot = UserSlot('Carson', 1)
obj_tuple = UserTuple('Carson', 1)
time_normal = min(timeit.repeat(get_fn(obj, 'name'), repeat=20))
print(time_normal) # 0.12550550000000005
time_slot = min(timeit.repeat(get_fn(obj_slot, 'name'), repeat=20))
print(time_slot) # 0.1368690000000008
time_tuple = min(timeit.repeat(get_fn(obj_tuple, 'name'), repeat=20))
print(time_tuple) # 0.16006120000000124
print(time_tuple/time_slot) # 1.1694481584580898 # The slot is almost 17% faster than NamedTuple on Windows. (Python 3.7.7)
If your __dict__ is not using, please choose between __slots__ (higher performance and storage) and NamedTuple (clear for reading and use)
You can review this link(Usage of slots
) to get more __slots__ information.
https://stackoverflow.com/a/32448434/159695 does not work in Python3.
https://stackoverflow.com/a/35993/159695 works in Python3.
And I extends it to add default values.
class myStruct:
def __init__(self, **kwds):
self.x=0
self.__dict__.update(kwds) # Must be last to accept assigned member variable.
def __repr__(self):
args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
return '%s(%s)' % ( self.__class__.__qualname__, ', '.join(args) )
a=myStruct()
b=myStruct(x=3,y='test')
c=myStruct(x='str')
>>> a
myStruct(x=0)
>>> b
myStruct(x=3, y='test')
>>> c
myStruct(x='str')
The following solution to a struct is inspired by the namedtuple implementation and some of the previous answers. However, unlike the namedtuple it is mutable, in it's values, but like the c-style struct immutable in the names/attributes, which a normal class or dict isn't.
_class_template = """\
class {typename}:
def __init__(self, *args, **kwargs):
fields = {field_names!r}
for x in fields:
setattr(self, x, None)
for name, value in zip(fields, args):
setattr(self, name, value)
for name, value in kwargs.items():
setattr(self, name, value)
def __repr__(self):
return str(vars(self))
def __setattr__(self, name, value):
if name not in {field_names!r}:
raise KeyError("invalid name: %s" % name)
object.__setattr__(self, name, value)
"""
def struct(typename, field_names):
class_definition = _class_template.format(
typename = typename,
field_names = field_names)
namespace = dict(__name__='struct_%s' % typename)
exec(class_definition, namespace)
result = namespace[typename]
result._source = class_definition
return result
Usage:
Person = struct('Person', ['firstname','lastname'])
generic = Person()
michael = Person('Michael')
jones = Person(lastname = 'Jones')
In [168]: michael.middlename = 'ben'
Traceback (most recent call last):
File "<ipython-input-168-b31c393c0d67>", line 1, in <module>
michael.middlename = 'ben'
File "<string>", line 19, in __setattr__
KeyError: 'invalid name: middlename'
If you don't have a 3.7 for #dataclass and need mutability, the following code might work for you. It's quite self-documenting and IDE-friendly (auto-complete), prevents writing things twice, is easily extendable and it is very simple to test that all instance variables are completely initialized:
class Params():
def __init__(self):
self.var1 : int = None
self.var2 : str = None
def are_all_defined(self):
for key, value in self.__dict__.items():
assert (value is not None), "instance variable {} is still None".format(key)
return True
params = Params()
params.var1 = 2
params.var2 = 'hello'
assert(params.are_all_defined)
The best way I found to do this was to use a custom dictionary class as explained in this post: https://stackoverflow.com/a/14620633/8484485
If iPython autocompletion support is needed, simply define the dir() function like this:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
def __dir__(self):
return self.keys()
You then define your pseudo struct like so: (this one is nested)
my_struct=AttrDict ({
'com1':AttrDict ({
'inst':[0x05],
'numbytes':2,
'canpayload':False,
'payload':None
})
})
You can then access the values inside my_struct like this:
print(my_struct.com1.inst)
=>[5]
The cleanest way I can think of is to use a class decorator that lets you declare a static class and rewrite it to act as a struct with normal, named properties:
from as_struct import struct
#struct
class Product():
name = 'unknown product'
quantity = -1
sku = '-'
# create instance
p = Product('plush toy', sku='12-345-6789')
# check content:
p.name # plush toy
p.quantity # -1
p.sku # 12-345-6789
Using the following decorator code:
def struct(struct_class):
# create a new init
def struct_init(self, *args, **kwargs):
i = 0 # we really don't need enumerate() here...
for value in args:
name = member_names[i]
default_value = member_values[i]
setattr(self, name, value if value is not None else default_value)
i += 1 # ...we just need to inc an int
for key,value in kwargs.items():
i = member_names.index(key)
default_value = member_values[i]
setattr(self, key, value if value is not None else default_value)
# extract the struct members
member_names = []
member_values = []
for attr_name in dir(struct_class):
if not attr_name.startswith('_'):
value = getattr(struct_class, attr_name)
if not callable(value):
member_names.append(attr_name)
member_values.append(value)
# rebind and return
struct_class.init = struct_init
return struct_class
Which works by taking the class, extracting the field names and their default values, then rewriting the class's __init__ function to set self attributes based on knowing which argument index maps to which property name.
I think Python structure dictionary is suitable for this requirement.
d = dict{}
d[field1] = field1
d[field2] = field2
d[field2] = field3
Extending #gz.'s (generally superior to this one) answer, for a quick and dirty namedtuple structure we can do:
import collections
x = collections.namedtuple('foobar', 'foo bar')(foo=1,bar=2)
y = collections.namedtuple('foobar', 'foo bar')(foo=3,bar=4)
print(x,y)
>foobar(foo=1, bar=2) foobar(foo=3, bar=4)
Related
How to get all values of variables in class?
class NiceClass(): some_value = SomeObject(...) some_other_value = SomeOtherObject(...) #classmethod def get_all_vars(cls): ... I want get_all_vars() to return [SomeObject(...), SomeOtherObject(...)], or more specifically, the values of the variables in cls. Solutions tried that didn't work out for me: return [cls.some_value, cls.some_other_value, ...] (requires listing the variable manually) subclassing Enum then using list(cls) (requires using some_value.value to access the value elsewhere in the program, also type hinting would be a mess) namedtuples (nope not touching that subject, heard it was much more complicated than Enum) [value for key, value in vars(cls).items() if not callable(value) and not key.startswith("__")] (too hacky due to using vars(cls), also for some reason it also includes get_all_vars due to it being a classmethod)
There are two ways. This is a straight answer to your question: class Foo: pass class Bar: x: int = 1 y: str = 'hello' z: Foo = Foo() #classmethod def get_all(cls): xs = [] for name, value in vars(cls).items(): if not (name.startswith('__') or isinstance(value, classmethod)): xs.append(value) return xs This is what I suggest: from dataclasses import dataclass, fields class Foo: pass #dataclass class Bar: x: int = 1 y: str = 'hello' z: Foo = Foo() #classmethod def get_defaults(cls): return [f.default for f in fields(cls)] #classmethod def get_all(cls): return [getattr(cls, f.name) for f in fields(cls)] results: Bar.get_defaults() == Bar.get_all() # True -> [1, 'hello', __main__.Foo] Bar.x = 10 Bar.get_defaults() == Bar.get_all() # False -> [1, 'hello', __main__.Foo] != [10, 'hello', __main__.Foo]
You can create a list of values and define individual attributes at the same time with minimal boilerplate. class NiceClass(): (some_value, some_other_value, ) = _all_values = [ SomeObject(...), SomeOtherObject(...) ] #classmethod def get_all_vars(cls): return cls._all_values The most obvious drawback to this is that it's easy to get the order of names and values out of sync. Ideally, you might like to do something like class NiceClass: _attributes = { 'some_value': SomeObject(...), 'some_other_value': SomeOtherObject(...) } #classmethod def get_all_vars(cls): return cls._attributes.values() and have some way to "inject" the contents of _attributes into the class namespace directly. The simplest way to do this is with a mix-in class that defines __init_subclass__: class AddAttributes: def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls.__dict__.update(cls._attributes) class NiceClass(AddAttributes): # As above ...
This might sound like a https://xyproblem.info/ but my solution might work in the other case as well. You can get the fields of an object by using __dict__ or vars (which is considered more pythonic given: Python dictionary from an object's fields) You could do something like: class ClassTest: def __init__(self): self.one = 1 self.two = 2 list(vars(ClassTest()).values()) But you will see that it has some limitations. It doesn't recognize the not in self.variable_name defined variables like you have used above. It might help you nonetheless, because you can simply define them in init.
How to implement structure data type in python [duplicate]
Is there a way to conveniently define a C-like structure in Python? I'm tired of writing stuff like: class MyStruct(): def __init__(self, field1, field2, field3): self.field1 = field1 self.field2 = field2 self.field3 = field3
Update: Data Classes With the introduction of Data Classes in Python 3.7 we get very close. The following example is similar to the NamedTuple example below, but the resulting object is mutable and it allows for default values. from dataclasses import dataclass #dataclass class Point: x: float y: float z: float = 0.0 p = Point(1.5, 2.5) print(p) # Point(x=1.5, y=2.5, z=0.0) This plays nicely with the new typing module in case you want to use more specific type annotations. I've been waiting desperately for this! If you ask me, Data Classes and the new NamedTuple declaration, combined with the typing module are a godsend! Improved NamedTuple declaration Since Python 3.6 it became quite simple and beautiful (IMHO), as long as you can live with immutability. A new way of declaring NamedTuples was introduced, which allows for type annotations as well: from typing import NamedTuple class User(NamedTuple): name: str class MyStruct(NamedTuple): foo: str bar: int baz: list qux: User my_item = MyStruct('foo', 0, ['baz'], User('peter')) print(my_item) # MyStruct(foo='foo', bar=0, baz=['baz'], qux=User(name='peter'))
Use a named tuple, which was added to the collections module in the standard library in Python 2.6. It's also possible to use Raymond Hettinger's named tuple recipe if you need to support Python 2.4. It's nice for your basic example, but also covers a bunch of edge cases you might run into later as well. Your fragment above would be written as: from collections import namedtuple MyStruct = namedtuple("MyStruct", "field1 field2 field3") The newly created type can be used like this: m = MyStruct("foo", "bar", "baz") You can also use named arguments: m = MyStruct(field1="foo", field2="bar", field3="baz")
You can use a tuple for a lot of things where you would use a struct in C (something like x,y coordinates or RGB colors for example). For everything else you can use dictionary, or a utility class like this one: >>> class Bunch: ... def __init__(self, **kwds): ... self.__dict__.update(kwds) ... >>> mystruct = Bunch(field1=value1, field2=value2) I think the "definitive" discussion is here, in the published version of the Python Cookbook.
Perhaps you are looking for Structs without constructors: class Sample: name = '' average = 0.0 values = None # list cannot be initialized here! s1 = Sample() s1.name = "sample 1" s1.values = [] s1.values.append(1) s1.values.append(2) s1.values.append(3) s2 = Sample() s2.name = "sample 2" s2.values = [] s2.values.append(4) for v in s1.values: # prints 1,2,3 --> OK. print v print "***" for v in s2.values: # prints 4 --> OK. print v
How about a dictionary? Something like this: myStruct = {'field1': 'some val', 'field2': 'some val'} Then you can use this to manipulate values: print myStruct['field1'] myStruct['field2'] = 'some other values' And the values don't have to be strings. They can be pretty much any other object.
dF: that's pretty cool... I didn't know that I could access the fields in a class using dict. Mark: the situations that I wish I had this are precisely when I want a tuple but nothing as "heavy" as a dictionary. You can access the fields of a class using a dictionary because the fields of a class, its methods and all its properties are stored internally using dicts (at least in CPython). ...Which leads us to your second comment. Believing that Python dicts are "heavy" is an extremely non-pythonistic concept. And reading such comments kills my Python Zen. That's not good. You see, when you declare a class you are actually creating a pretty complex wrapper around a dictionary - so, if anything, you are adding more overhead than by using a simple dictionary. An overhead which, by the way, is meaningless in any case. If you are working on performance critical applications, use C or something.
I would also like to add a solution that uses slots: class Point: __slots__ = ["x", "y"] def __init__(self, x, y): self.x = x self.y = y Definitely check the documentation for slots but a quick explanation of slots is that it is python's way of saying: "If you can lock these attributes and only these attributes into the class such that you commit that you will not add any new attributes once the class is instantiated (yes you can add new attributes to a class instance, see example below) then I will do away with the large memory allocation that allows for adding new attributes to a class instance and use just what I need for these slotted attributes". Example of adding attributes to class instance (thus not using slots): class Point: def __init__(self, x, y): self.x = x self.y = y p1 = Point(3,5) p1.z = 8 print(p1.z) Output: 8 Example of trying to add attributes to class instance where slots was used: class Point: __slots__ = ["x", "y"] def __init__(self, x, y): self.x = x self.y = y p1 = Point(3,5) p1.z = 8 Output: AttributeError: 'Point' object has no attribute 'z' This can effectively works as a struct and uses less memory than a class (like a struct would, although I have not researched exactly how much). It is recommended to use slots if you will be creating a large amount of instances of the object and do not need to add attributes. A point object is a good example of this as it is likely that one may instantiate many points to describe a dataset.
You can subclass the C structure that is available in the standard library. The ctypes module provides a Structure class. The example from the docs: >>> 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 >>>
You can also pass the init parameters to the instance variables by position # Abstract struct class class Struct: def __init__ (self, *argv, **argd): if len(argd): # Update by dictionary self.__dict__.update (argd) else: # Update by position attrs = filter (lambda x: x[0:2] != "__", dir(self)) for n in range(len(argv)): setattr(self, attrs[n], argv[n]) # Specific class class Point3dStruct (Struct): x = 0 y = 0 z = 0 pt1 = Point3dStruct() pt1.x = 10 print pt1.x print "-"*10 pt2 = Point3dStruct(5, 6) print pt2.x, pt2.y print "-"*10 pt3 = Point3dStruct (x=1, y=2, z=3) print pt3.x, pt3.y, pt3.z print "-"*10
Whenever I need an "instant data object that also behaves like a dictionary" (I don't think of C structs!), I think of this cute hack: class Map(dict): def __init__(self, **kwargs): super(Map, self).__init__(**kwargs) self.__dict__ = self Now you can just say: struct = Map(field1='foo', field2='bar', field3=42) self.assertEquals('bar', struct.field2) self.assertEquals(42, struct['field3']) Perfectly handy for those times when you need a "data bag that's NOT a class", and for when namedtuples are incomprehensible...
Some the answers here are massively elaborate. The simplest option I've found is (from: http://norvig.com/python-iaq.html): class Struct: "A structure that can have any fields defined." def __init__(self, **entries): self.__dict__.update(entries) Initialising: >>> options = Struct(answer=42, linelen=80, font='courier') >>> options.answer 42 adding more: >>> options.cat = "dog" >>> options.cat dog edit: Sorry didn't see this example already further down.
You access C-Style struct in python in following way. class cstruct: var_i = 0 var_f = 0.0 var_str = "" if you just want use object of cstruct 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 of 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 'cstruct' name, please use your struct name instead of var_i, var_f, var_str, please define your structure's member variable.
This might be a bit late but I made a solution using Python Meta-Classes (decorator version below too). When __init__ is called during run time, it grabs each of the arguments and their value and assigns them as instance variables to your class. This way you can make a struct-like class without having to assign every value manually. My example has no error checking so it is 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 Here it is in action. >>> class MyClass(object): __metaclass__ = MyStruct def __init__(self, a, b, c): pass >>> my_instance = MyClass(1, 2, 3) >>> my_instance.a 1 >>> I posted it on reddit and /u/matchu posted a decorator version which is cleaner. I'd encourage you to use it unless you want to expand 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 >>>
I wrote a decorator which you can use on any method to make it so that all of the arguments passed in, or any defaults, are assigned 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 A quick demonstration. Note that I use a positional argument a, use the default value for b, and a named argument c. I then print all 3 referencing self, to show that they've been properly assigned before the method is entered. 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') Note that my decorator should work with any method, not just __init__.
I don't see this answer here, so I figure I'll add it since I'm leaning Python right now and just discovered it. The Python tutorial (Python 2 in this case) gives the following simple and effective example: class Employee: pass john = Employee() # Create an empty employee record # Fill the fields of the record john.name = 'John Doe' john.dept = 'computer lab' john.salary = 1000 That is, an empty class object is created, then instantiated, and the fields are added dynamically. The up-side to this is its really simple. The downside is it isn't particularly self-documenting (the intended members aren't listed anywhere in the class "definition"), and unset fields can cause problems when accessed. Those two problems can be solved by: class Employee: def __init__ (self): self.name = None # or whatever self.dept = None self.salary = None Now at a glance you can at least see what fields the program will be expecting. Both are prone to typos, john.slarly = 1000 will succeed. Still, it works.
Here is a solution which uses a class (never instantiated) to hold data. I like that this way involves very little typing and does not require any additional packages etc. class myStruct: field1 = "one" field2 = "2" You can add more fields later, as needed: myStruct.field3 = 3 To get the values, the fields are accessed as usual: >>> myStruct.field1 'one'
Personally, I like this variant too. It extends #dF's answer. class struct: def __init__(self, *sequential, **named): fields = dict(zip(sequential, [None]*len(sequential)), **named) self.__dict__.update(fields) def __repr__(self): return str(self.__dict__) It supports two modes of initialization (that can be blended): # Struct with field1, field2, field3 that are initialized to None. mystruct1 = struct("field1", "field2", "field3") # Struct with field1, field2, field3 that are initialized according to arguments. mystruct2 = struct(field1=1, field2=2, field3=3) Also, it prints nicer: print(mystruct2) # Prints: {'field3': 3, 'field1': 1, 'field2': 2}
There is a python package exactly for this purpose. see cstruct2py cstruct2py is a pure python library for generate python classes from C code and use them to pack and unpack data. The library can parse C headres (structs, unions, enums, and arrays declarations) and emulate them in python. The generated pythonic classes can parse and pack the data. For example: typedef struct { int x; int y; } Point; after generating pythonic class... p = Point(x=0x1234, y=0x5678) p.packed == "\x34\x12\x00\x00\x78\x56\x00\x00" How to use First we need to generate the pythonic structs: import cstruct2py parser = cstruct2py.c2py.Parser() parser.parse_file('examples/example.h') Now we can import all names from the C code: parser.update_globals(globals()) We can also do that directly: A = parser.parse_string('struct A { int x; int y;};') Using types and defines from the C code a = A() a.x = 45 print a buf = a.packed b = A(buf) print b c = A('aaaa11112222', 2) print c print repr(c) The output will be: {'x':0x2d, 'y':0x0} {'x':0x2d, 'y':0x0} {'x':0x31316161, 'y':0x32323131} A('aa111122', x=0x31316161, y=0x32323131) Clone For clone cstruct2py run: git clone https://github.com/st0ky/cstruct2py.git --recursive
Here is a quick and dirty trick: >>> ms = Warning() >>> ms.foo = 123 >>> ms.bar = 'akafrit' How does it works? It just re-use the builtin class Warning (derived from Exception) and use it as it was you own defined class. The good points are that you do not need to import or define anything first, that "Warning" is a short name, and that it also makes clear you are doing something dirty which should not be used elsewhere than a small script of yours. By the way, I tried to find something even simpler like ms = object() but could not (this last exemple is not working). If you have one, I am interested.
NamedTuple is comfortable. but there no one shares the performance and storage. from typing import NamedTuple import guppy # pip install guppy import timeit class User: def __init__(self, name: str, uid: int): self.name = name self.uid = uid class UserSlot: __slots__ = ('name', 'uid') def __init__(self, name: str, uid: int): self.name = name self.uid = uid class UserTuple(NamedTuple): # __slots__ = () # AttributeError: Cannot overwrite NamedTuple attribute __slots__ name: str uid: int def get_fn(obj, attr_name: str): def get(): getattr(obj, attr_name) return get if 'memory test': obj = [User('Carson', 1) for _ in range(1000000)] # Cumulative: 189138883 obj_slot = [UserSlot('Carson', 1) for _ in range(1000000)] # 77718299 <-- winner obj_namedtuple = [UserTuple('Carson', 1) for _ in range(1000000)] # 85718297 print(guppy.hpy().heap()) # Run this function individually. """ Index Count % Size % Cumulative % Kind (class / dict of class) 0 1000000 24 112000000 34 112000000 34 dict of __main__.User 1 1000000 24 64000000 19 176000000 53 __main__.UserTuple 2 1000000 24 56000000 17 232000000 70 __main__.User 3 1000000 24 56000000 17 288000000 87 __main__.UserSlot ... """ if 'performance test': obj = User('Carson', 1) obj_slot = UserSlot('Carson', 1) obj_tuple = UserTuple('Carson', 1) time_normal = min(timeit.repeat(get_fn(obj, 'name'), repeat=20)) print(time_normal) # 0.12550550000000005 time_slot = min(timeit.repeat(get_fn(obj_slot, 'name'), repeat=20)) print(time_slot) # 0.1368690000000008 time_tuple = min(timeit.repeat(get_fn(obj_tuple, 'name'), repeat=20)) print(time_tuple) # 0.16006120000000124 print(time_tuple/time_slot) # 1.1694481584580898 # The slot is almost 17% faster than NamedTuple on Windows. (Python 3.7.7) If your __dict__ is not using, please choose between __slots__ (higher performance and storage) and NamedTuple (clear for reading and use) You can review this link(Usage of slots ) to get more __slots__ information.
https://stackoverflow.com/a/32448434/159695 does not work in Python3. https://stackoverflow.com/a/35993/159695 works in Python3. And I extends it to add default values. class myStruct: def __init__(self, **kwds): self.x=0 self.__dict__.update(kwds) # Must be last to accept assigned member variable. def __repr__(self): args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()] return '%s(%s)' % ( self.__class__.__qualname__, ', '.join(args) ) a=myStruct() b=myStruct(x=3,y='test') c=myStruct(x='str') >>> a myStruct(x=0) >>> b myStruct(x=3, y='test') >>> c myStruct(x='str')
The following solution to a struct is inspired by the namedtuple implementation and some of the previous answers. However, unlike the namedtuple it is mutable, in it's values, but like the c-style struct immutable in the names/attributes, which a normal class or dict isn't. _class_template = """\ class {typename}: def __init__(self, *args, **kwargs): fields = {field_names!r} for x in fields: setattr(self, x, None) for name, value in zip(fields, args): setattr(self, name, value) for name, value in kwargs.items(): setattr(self, name, value) def __repr__(self): return str(vars(self)) def __setattr__(self, name, value): if name not in {field_names!r}: raise KeyError("invalid name: %s" % name) object.__setattr__(self, name, value) """ def struct(typename, field_names): class_definition = _class_template.format( typename = typename, field_names = field_names) namespace = dict(__name__='struct_%s' % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition return result Usage: Person = struct('Person', ['firstname','lastname']) generic = Person() michael = Person('Michael') jones = Person(lastname = 'Jones') In [168]: michael.middlename = 'ben' Traceback (most recent call last): File "<ipython-input-168-b31c393c0d67>", line 1, in <module> michael.middlename = 'ben' File "<string>", line 19, in __setattr__ KeyError: 'invalid name: middlename'
If you don't have a 3.7 for #dataclass and need mutability, the following code might work for you. It's quite self-documenting and IDE-friendly (auto-complete), prevents writing things twice, is easily extendable and it is very simple to test that all instance variables are completely initialized: class Params(): def __init__(self): self.var1 : int = None self.var2 : str = None def are_all_defined(self): for key, value in self.__dict__.items(): assert (value is not None), "instance variable {} is still None".format(key) return True params = Params() params.var1 = 2 params.var2 = 'hello' assert(params.are_all_defined)
The best way I found to do this was to use a custom dictionary class as explained in this post: https://stackoverflow.com/a/14620633/8484485 If iPython autocompletion support is needed, simply define the dir() function like this: class AttrDict(dict): def __init__(self, *args, **kwargs): super(AttrDict, self).__init__(*args, **kwargs) self.__dict__ = self def __dir__(self): return self.keys() You then define your pseudo struct like so: (this one is nested) my_struct=AttrDict ({ 'com1':AttrDict ({ 'inst':[0x05], 'numbytes':2, 'canpayload':False, 'payload':None }) }) You can then access the values inside my_struct like this: print(my_struct.com1.inst) =>[5]
The cleanest way I can think of is to use a class decorator that lets you declare a static class and rewrite it to act as a struct with normal, named properties: from as_struct import struct #struct class Product(): name = 'unknown product' quantity = -1 sku = '-' # create instance p = Product('plush toy', sku='12-345-6789') # check content: p.name # plush toy p.quantity # -1 p.sku # 12-345-6789 Using the following decorator code: def struct(struct_class): # create a new init def struct_init(self, *args, **kwargs): i = 0 # we really don't need enumerate() here... for value in args: name = member_names[i] default_value = member_values[i] setattr(self, name, value if value is not None else default_value) i += 1 # ...we just need to inc an int for key,value in kwargs.items(): i = member_names.index(key) default_value = member_values[i] setattr(self, key, value if value is not None else default_value) # extract the struct members member_names = [] member_values = [] for attr_name in dir(struct_class): if not attr_name.startswith('_'): value = getattr(struct_class, attr_name) if not callable(value): member_names.append(attr_name) member_values.append(value) # rebind and return struct_class.init = struct_init return struct_class Which works by taking the class, extracting the field names and their default values, then rewriting the class's __init__ function to set self attributes based on knowing which argument index maps to which property name.
I think Python structure dictionary is suitable for this requirement. d = dict{} d[field1] = field1 d[field2] = field2 d[field2] = field3
Extending #gz.'s (generally superior to this one) answer, for a quick and dirty namedtuple structure we can do: import collections x = collections.namedtuple('foobar', 'foo bar')(foo=1,bar=2) y = collections.namedtuple('foobar', 'foo bar')(foo=3,bar=4) print(x,y) >foobar(foo=1, bar=2) foobar(foo=3, bar=4)
Setting default values in a class
I am creating a class in Python, and I am unsure how to properly set default values. My goal is to set default values for all class instances, which can also be modified by a class method. However, I would like to have the initial default values restored after calling a method. I have been able to make it work with the code shown below. It isn't very "pretty", so I suspect that are better approaches to this problem. class plots: def __init__(self, **kwargs): self.default_attr = {'a': 1, 'b': 2, 'c': 3} self.default_attr.update(kwargs) self.__dict__.update((k, v) for k, v in self.default_attr.items()) def method1(self, **kwargs): self.__dict__.update((k, v) for k, v in kwargs.items()) #### Code for this method goes here # Then restore initial default values self.__dict__.update((k, v) for k, v in self.default_attr.items()) When I use this class, I would do something like my_instance = plots() and my_instance.method1(), my_instance.method1(b = 5), and my_instance.method1(). When calling method1 the third time, b would be 5 if I don't reset the default values at the end of the method definition, but I would like it to be 2 again. Note: the code above is just an example. The real class has dozens of default values, and using all of them as input arguments would be considered an antipattern. Any suggestion on how to properly address this issue?
You can use class variables, and property to achieve your goal to set default values for all class instances. The instances values can be modified directly, and the initial default values restored after calling a method. In view of the context that "the real class has dozens of default values", another approach that you may consider, is to set up a configuration file containing the default values, and using this file to initialize, or reset the defaults. Here is a short example of the first approach using one class variable: class Plots: _a = 1 def __init__(self): self._a = None self.reset_default_values() def reset_default_values(self): self._a = Plots._a #property def a(self): return self._a #a.setter def a(self, value): self._a = value plot = Plots() print(plot.a) plot.a = 42 print(plot.a) plot.reset_default_values() print(plot.a) output: 1 42 1
There is a whole bunch of ways to solve this problem, but if you have python 3.7 installed (or have 3.6 and install the backport), dataclasses might be a good fit for a nice solution. First of all, it lets you define the default values in a readable and compact manner, and also allows all the mutation operations you need: >>> from dataclasses import dataclass >>> #dataclass ... class Plots: ... a: int = 1 ... b: int = 2 ... c: int = 3 ... >>> p = Plots() # create a Plot with only default values >>> p Plots(a=1, b=2, c=3) >>> p.a = -1 # update something in this Plot instance >>> p Plots(a=-1, b=2, c=3) You also get the option to define default factories instead of default values for free with the dataclass field definition. It might not be a problem yet, but it avoids the mutable default value gotcha, which every python programmer runs into sooner or later. Last but not least, writing a reset function is quite easy given an existing dataclass, because it keeps track of all the default values already in its __dataclass_fields__ attribute: >>> from dataclasses import dataclass, MISSING >>> #dataclass ... class Plots: ... a: int = 1 ... b: int = 2 ... c: int = 3 ... ... def reset(self): ... for name, field in self.__dataclass_fields__.items(): ... if field.default != MISSING: ... setattr(self, name, field.default) ... else: ... setattr(self, name, field.default_factory()) ... >>> p = Plots(a=-1) # create a Plot with some non-default values >>> p Plots(a=-1, b=2, c=3) >>> p.reset() # calling reset on it restores the pre-defined defaults >>> p Plots(a=1, b=2, c=3) So now you can write some function do_stuff(...) that updates the fields in a Plot instance, and as long as you execute reset() the changes won't persist.
You can use a context manager or a decorator to apply and reset the values without having to type the same code on each method. Rather than having self.default_attr, I'd just return to the previous state. Using a decorator you could get: def with_kwargs(fn): def inner(self, **kwargs): prev = self.__dict__.copy() try: self.__dict__.update(kwargs) ret = fn(self) finally: self.__dict__ = prev return ret return inner class plots: a = 1 b = 2 c = 3 def __init__(self, **kwargs): self.__dict__.update(kwargs) #with_kwargs def method1(self): # Code goes here IMHO this is a bad idea, and would at least suggest not mutating plots. You can do this by making a new object and passing that to method1 as self. class Transparent: pass def with_kwargs(fn): def inner(self, **kwargs): new_self = Transparent() new_self.__dict__ = {**self.__dict__, **kwargs} return fn(new_self) return inner
Setting a member variable string whose value is its name
I want to replace string literals in my code, as I want to minimize risk of typos, especially in dict key sets: a['typoh'] = 'this is bad' I don't want to type things in twice (risk of a missed typo on the value) I want it to be "trackable" by various IDEs (i.e. click thru to see where it is defined and escape completion). Enums are out: 'E.a.name' to get 'a' is dumb. I have been told this can be done with slots, but I can't figure out how without a little trickery. I can think of a few ways below: This is an unacceptable answer: class TwiceIsNotNice(object): this_is_a_string = 'this_is_a_string' ... (five thousand string constants in) this_has_a_hard_to_spot_typographical_error = 'this_has_a_had_to_spot_typographical_error' ... (five thousand more string constants) A clear but annoying way is with a "Stringspace" class/object where the attributes are set via a string list passed in. This solves the minimized typo risk, is VERY easy to read, but has neither IDE trackability nor autocompletion. It's okay, but makes people complain (please don't complain here, I am simply showing how it could be done): string_consts = Stringspace('a', 'b',...,'asdfasdfasdf') print(string_consts.a) ... where: class Stringspace(object): def __init__(self, *strlist): for s in strlist: setattr(self, s, s) Another way is to define a class using a sentinel object, setting the value in a post phase. This is okay, is trackable, presents itself as an actual class, allows for aliases, etc. But it requires an annoying extra call at the end of the class: same = object() class StrList(object): this_is_a_strval = same this_is_another_strval = same this_gets_aliased = "to something else" # This would of course could become a function for attr in dir(StrList): if getattr(StrList, attr) is same: setattr(StrList, attr, attr) print(StrList.a) If this is what the slot magic is supposedly about, then I am disappointed, as one would have to actually instantiate an object: class SlotEnum(object): __slots__ = [] def __init__(self): for k in self.__slots__: setattr(self, k, k) class Foo(SlotEnum): __slots__ = ['a', 'b'] foo_enum_OBJECT = Foo() print(foo_enum_OBJECT.a)
Enums are out: E.a.name to get a is dumb. from enum import Enum, auto class StrEnum(str, Enum): "base class for Enum members to be strings matching the member's name" def __repr__(self): return '<%s.%s>' % (self.__class__.__name__, self.name) def __str__(self): return self.name class E(StrEnum): a = auto() this_is_a_string = auto() no_typo_here = auto() >>> print(repr(E.a)) <E.a> >>> print(E.a) a >>> print('the answer is: %s!' % E.a) the answer is: a!
I found one solution at this external link using a custom meta class, for your class containing the string member variables: Step 1 of 2: The custom meta class can be defined like this: class MetaForMyStrConstants(type): def __new__(metacls, cls, bases, classdict): object_attrs = set(dir(type(cls, (object,), {}))) simple_enum_cls = super().__new__(metacls, cls, bases, classdict) simple_enum_cls._member_names_ = set(classdict.keys()) - object_attrs non_members = set() for attr in simple_enum_cls._member_names_: if attr.startswith('_') and attr.endswith('_'): non_members.add(attr) else: setattr(simple_enum_cls, attr, attr) simple_enum_cls._member_names_.difference_update(non_members) return simple_enum_cls Step 2 of 2: The class defining your strings can be defined like this (with dummy values, eg, empty tuples): class MyStrConstants(metaclass=MetaForMyStrConstants): ONE_LONG_STR = () ANOTHER_LONG_STR = () THE_REAL_LONGEST_STR = () Testing it out: print (MyStrConstants.ONE_LONG_STR) print (MyStrConstants.ANOTHER_LONG_STR) print (MyStrConstants.THE_REAL_LONGEST_STR) Output: ONE_LONG_STR ANOTHER_LONG_STR THE_REAL_LONGEST_STR
Extend ctypes to specify field overloading
I would like to extend ctypes Structure, BigEndianStructure, LittleEndianStructure. The ability to specify each field to have a description, and overload how the variable is returned to possibly enum, polyco, etc attributes. Something like the following is what I would like to do, but am not sure how to make the ModifedCTypesStructure parent class. My goal is to use this for commanding / telemetry of binary data. class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 class Packet(ModifedCTypesStructure): __fields__ = [("fieldA",ctypes.c_int32, {"brief":"""Description of fieldA""", "enum":Color} ), ("fieldB",ctypes.c_uint32, {"repr":lambda x: hex(x)} ) ] a = Packet() help(a.fieldA) > Description of fieldA a.fieldA = Color.RED print a._fieldA # Return the binary field > 1 a.fieldB = 0xbb print a.fieldB > 0xbb #Note repr is called to return '0xbb' print a._fieldB > 187
It is possible - most of the magic provided by ctypes.Structure is due to its fields being "descriptors" - i.e. objects that follow Python's descriptor protocol - analog to what we get when we use the #property decorator in a class body. ctypes.Structure has a metaclass that is responsible to convert each field listed in the special cased variable name _fields_ into a _ctypes.CField object (you can check that by verifying the result of type(mystryct.field) in an interactive Python prompt. Therefore, in order to extend the behavior for the fields themselves, we'd need to extend this CField class - and modify the metaclass that creates your Strutcture to use our fields. The CField class seems to be a normal Python class itself - so it is easy to modify, if we respect the call to the super-methods. however there are some catches in your "wishlist": using "help" requires the Python object to have the help string embedded in its class __doc__ attribute (not the instance). So we can do that each time the field itself is retrieved from the structure class, we ceae dynamically a new class with the required help. When retrieving a value from an object, Python can't "know" in advance if the value will be used just for being "viewed" by repr or will actually be used. Therefore we either customize the value that is returned by a.fieldB for one that have a custom representation or we don't do it at all. The code bellow does create a dynamic class on field retrieval that will have a custom representation, and try to preserve all other numeric properties of the underlying value. But that is set to be both slow and may present some incompatibilities - you may choose to turn that off when not debugging values, or simply get the raw value. Ctype's "Fields" will of course have some internal structure of their own, like the offset for each memory position and so on - thus, I'd suggest the following approach:(1) create a new "Field" class that does not inherit from ctypes.Field at all - and that implement the enhancements you want; (2) Upon ModifiedStructure creation create all the "_" prefixed names, and pass these for the original Ctypes.Structure metaclass to create its fields as it always does; (3) make our "Field" class read and write to the original ctypes.Fields, and have their custom transforms and representations. As you can see, I also took care of actually transforming Enum values upon writting. To try everything, just inherit from "ModifiedStructure" bellow, instead of ctypes.Structure: from ctypes import Structure import ctypes class A(Structure): _fields_ = [("a", ctypes.c_uint8)] FieldType = type(A.a) StructureType = type(A) del A def repr_wrapper(value, transform): class ReprWrapper(type(value)): def __new__(cls, value): return super().__new__(cls, value) def __repr__(self): return transform(self) return ReprWrapper(value) def help_wrapper(field): class Field2(field.__class__): __doc__ = field.help def __repr__(self): return self.__doc__ return Field2(field.name, field.type_, help=field.help, repr=field.repr, enum=field.enum) class Field: def __init__(self, name, type_, **kwargs): self.name = name self.type_ = type_ self.real_name = "_" + name self.help = kwargs.pop("brief", f"Proxy structure field {name}") self.repr = kwargs.pop("repr", None) self.enum = kwargs.pop("enum", None) if self.enum: self.rev_enum = {constant.value:constant for constant in self.enum.__members__.values() } def __get__(self, instance, owner): if not instance: return help_wrapper(self) value = getattr(instance, self.real_name) if self.enum: return self.rev_enum[value] if self.repr: return repr_wrapper(value, self.repr) return value def __set__(self, instance, value): if self.enum: value = getattr(self.enum, value.name).value setattr(instance, self.real_name, value) class ModifiedStructureMeta(StructureType): def __new__(metacls, name, bases, namespace): _fields = namespace.get("_fields_", "") classic_fields = [] for field in _fields: # Create the set of descriptors for the new-style fields: name = field[0] namespace[name] = Field(name, field[1], **(field[2] if len(field) > 2 else {})) classic_fields.append(("_" + name, field[1])) namespace["_fields_"] = classic_fields return super().__new__(metacls, name, bases, namespace) class ModifiedStructure(ctypes.Structure, metaclass=ModifiedStructureMeta): __slots__ = () And testing it on the interactive prompt: In [165]: class A(ModifiedStructure): ...: _fields_ = [("b", ctypes.c_uint8, {"enum": Color, 'brief': "a color", }), ("c", ctypes.c_uint8, {"repr": hex})] ...: ...: In [166]: a = A() In [167]: a.c = 20 In [169]: a.c Out[169]: 0x14 In [170]: a.c = 256 In [171]: a.c Out[171]: 0x0 In [172]: a.c = 255 In [173]: a.c Out[173]: 0xff In [177]: a.b = Color.RED In [178]: a._b Out[178]: 1 In [180]: help(A.b) (shows full Field class help starting with the given description) In [181]: A.b Out[181]: a color
This is what I came up with after playing around with metaclasses for a while. I haven't ever used them so I wasnt sure if this was the correct approach. I couldnt figure out the repr thing. Im going to play around with your solution #jsbueno. I ended up building and appending properties to the classes on creation. Everything i read was saying not to use metaclasses 99% of the time so kind of wondering if I was going down the wrong path. Also I wanted to use the same metaclass for BigEndian / LittleEndian structures forgot to add that to the wish list. import ctypes def make_fget(key,enum=None): def fget(self): res = getattr(self, key) if enum != None: res = enum(res) return res return fget def make_fset(key): def fset(self, value): if isinstance(value,Enum): value = value.value setattr(self, key, value) return fset class PyGndTlmMeta(type): def __new__(cls,name,bases, dct): endian = ctypes.Structure if '_endian_' in dct: endian = dct['_endian_'] if not endian in [ctypes.Structure, ctypes.LittleEndianStructure, ctypes.BigEndianStructure]: pass #TODO throw error fields = [] attributes = dct.copy() for f in dct['_fields_']: fname = '_' + f[0] ftype = f[1] res = (fname,ftype) specs = {} #If 3rd argument is an integer than we are dealing with bitfields if len(f) >= 3 and type(f[2]) == int: res = (fname,ftype,f[2]) elif len(f) >= 3 and type(f[2]) == dict: specs = f[2] elif len(f) >= 4 and type(f[3]) == dict: specs = f[3] fields.append(res) enum = None if "enum" in specs: enum = specs['enum'] fget = make_fget(fname,enum=enum) fset = make_fset(fname) doc = "Not Set" if "brief" in specs: doc = specs["brief"] if "enum" in specs: #TODO use different getter/setter pass attributes[f[0]] = property(fget, fset,doc=doc) attributes['_fields_'] = fields bases = (endian,) x = type(name, bases, attributes) return x from enum import Enum class Color(Enum): RED = 1 GREEN = 2 BLUE = 3 class Packet( ): __metaclass__ = PyGndTlmMeta _endian_ = ctypes.BigEndianStructure _fields_ = [ ("a",ctypes.c_byte,{"brief":"testing"}), ("b",ctypes.c_int, {"enum":Color}) ] x = Packet()