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()

Categories