Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Class factory to produce simple struct-like classes?

While investigating Ruby I came across this to create a simple Struct-like class:

Person = Struct.new(:forname, :surname)
person1 = Person.new('John', 'Doe')
puts person1  #<struct Person forname="John", surname="Doe">

Which raised a few Python questions for me. I have written a [VERY] basic clone of this mechanism in Python:

def Struct(*args):
    class NewStruct:
        def __init__(self):
            for arg in args:
                self.__dict__[arg] = None

    return NewStruct

>>> Person = Struct('forename', 'surname')
>>> person1 = Person()
>>> person2 = Person()
>>> person1.forename, person1.surname = 'John','Doe'
>>> person2.forename, person2.surname = 'Foo','Bar'
>>> person1.forename
'John'
>>> person2.forename
'Foo'
  1. Is there already a similar mechanism in Python to handle this? (I usually just use dictionaries).

  2. How would I get the Struct() function to create the correct __init__() arguments. (in this case I would like to perform person1 = Person('John', 'Doe') Named Arguments if possible: person1 = Person(surname='Doe', forename='John')

I Would like, as a matter of interest, to have Question 2 answered even if there is a better Python mechanism to do this.

like image 864
kjfletch Avatar asked Aug 12 '09 07:08

kjfletch


People also ask

What is the Python equivalent of struct?

Python does not exactly have the same thing as a struct in Matlab. You can achieve something like it by defining an empty class and then defining attributes of the class. You can check if an object has a particular attribute using hasattr.

Are structs like classes C?

In C++, structs and classes are pretty much the same; the only difference is that where access modifiers (for member variables, methods, and base classes) in classes default to private, access modifiers in structs default to public.

Is a struct the same thing as a class?

The only difference between a struct and class in C++ is the default accessibility of member variables and methods. In a struct they are public; in a class they are private.

How is struct in C program different from class in Python?

Without taking pages and pages to go into the details, think of a C struct as a way to organize data, while a Python (or C++ or Objective-C) "class" is a way to organize not only your data, but the operations for that data.


2 Answers

If you're using Python 2.6, try the standard library namedtuple class.

>>> from collections import namedtuple
>>> Person = namedtuple('Person', ('forename', 'surname'))
>>> person1 = Person('John', 'Doe')
>>> person2 = Person(forename='Adam', surname='Monroe')
>>> person1.forename
'John'
>>> person2.surname
'Monroe'

Edit: As per comments, there is a backport for earlier versions of Python

like image 118
Alice Purcell Avatar answered Oct 17 '22 22:10

Alice Purcell


If you're running python <2.6 or would like to extend your class to do more stuff, I would suggest using the type() builtin. This has the advantage over your solution in that the setting up of __dict__ happens at class creation rather than instantiation. It also doesn't define an __init__ method and thus doesn't lead to strange behavior if the class calls __init__ again for some reason. For example:

def Struct(*args, **kwargs):
    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), kwargs)

Used like so:

>>> MyStruct = Struct("forename", "lastname")

Equivalent to:

class MyStruct(object):
    forename = None
    lastname = None

While this:

>>> TestStruct = Struct("forename", age=18, name="TestStruct")

Is equivalent to:

class TestStruct(object):
    forename = None
    age = 18

Update

Additionally, you can edit this code to very easily prevent assignment of other variables than the ones specificed. Just change the Struct() factory to assign __slots__.

def Struct(*args, **kwargs):
    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    kwargs['__slots__'] = kwargs.keys()
    return type(name, (object,), kwargs)
like image 11
André Eriksson Avatar answered Oct 17 '22 23:10

André Eriksson