Class factory to produce simple struct-like classes?

后端 未结 7 1404
盖世英雄少女心
盖世英雄少女心 2020-12-08 05:47

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

Person = Struct.new(:forname, :surname)
person1 = Person.new(\'John\', \'Do         


        
相关标签:
7条回答
  • 2020-12-08 06:13

    As others have said, named tuples in Python 2.6/3.x. With older versions, I usually use the Stuff class:

    class Stuff(object):
        def __init__(self, **kwargs):
            self.__dict__.update(kwargs)
    
    john = Stuff(forename='John', surname='Doe')
    

    This doesn't protect you from mispellings though. There's also a recipe for named tuples on ActiveState:

    http://code.activestate.com/recipes/500261/

    0 讨论(0)
  • 2020-12-08 06:13

    This is following up on Cide's answer (and probably only interesting for people who want to dig deeper).

    I experienced a problem using Cide's updated definition of Struct(), the one using __slots__. The problem is that instances of returned classes have read-only attributes:

    >>> MS = Struct('forename','lastname')
    >>> m=MS()
    >>> m.forename='Jack'
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'MyStruct' object attribute 'forename' is read-only
    

    Seems that __slots__ is blocking instance-level attributes when there are class attributes of same names. I've tried to overcome this by providing an __init__ method, so instance attributes can be set at object creation time:

    def Struct1(*args, **kwargs):
        def init(self):
            for k,v in kwargs.items():
                setattr(self, k, v)
        name = kwargs.pop("name", "MyStruct")
        kwargs.update(dict((k, None) for k in args))
        return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})
    

    As a net effect the constructed class only sees the __init__ method and the __slots__ member, which is working as desired:

    >>> MS1 = Struct1('forename','lastname')
    >>> m=MS1()
    >>> m.forename='Jack'
    >>> m.forename
    'Jack'
    
    0 讨论(0)
  • 2020-12-08 06:16

    The Python package esu brings a struct that can provide almost the same functionality:

    from esu import Struct
    
    Customer = Struct(
            'Customer',
            'name', 'address',
            methods={
                'greeting': lambda self: "Hello {}".format(self.__dict__['name'])
            })
    
    dave = Customer()
    dave.name = 'Dave'
    dave.greeting() # => Hello Dave
    

    from https://torokmark.github.io/post/python-struct/

    0 讨论(0)
  • 2020-12-08 06:19

    There is namedtuple

    >>> from collections import namedtuple
    >>> Person = namedtuple("Person", ("forename", "surname"))
    >>> john = Person("John", "Doe")
    >>> john.forename 
    'John'
    >>> john.surname 
    'Doe'
    
    0 讨论(0)
  • 2020-12-08 06:20

    An update of ThomasH's variant:

    def Struct(*args, **kwargs):
        def init(self, *iargs, **ikwargs):
            for k,v in kwargs.items():
                setattr(self, k, v)
            for i in range(len(iargs)):
                setattr(self, args[i], iargs[i])
            for k,v in ikwargs.items():
                setattr(self, k, v)
    
        name = kwargs.pop("name", "MyStruct")
        kwargs.update(dict((k, None) for k in args))
        return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})
    

    This allows parameters (and named parameters) passed into __init__() (without any validation - seems crude):

    >>> Person = Struct('fname', 'age')
    >>> person1 = Person('Kevin', 25)
    >>> person2 = Person(age=42, fname='Terry')
    >>> person1.age += 10
    >>> person2.age -= 10
    >>> person1.fname, person1.age, person2.fname, person2.age
    ('Kevin', 35, 'Terry', 32)
    >>> 
    

    Update

    Having a look into how namedtuple() does this in collections.py. The class is created and expanded as a string and evaluated. Also has support for pickling and so on, etc.

    0 讨论(0)
  • 2020-12-08 06:26

    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

    0 讨论(0)
提交回复
热议问题