How to create a new unknown or dynamic/expando object in Python

后端 未结 5 381
我在风中等你
我在风中等你 2021-02-02 07:07

In python how can we create a new object without having a predefined Class and later dynamically add properties to it ?

example:

dynamic_object = Dynami         


        
相关标签:
5条回答
  • 2021-02-02 07:38

    If you take metaclassing approach from @Martijn's answer, @Ned's answer can be rewritten shorter (though it's obviously less readable, but does the same thing).

    obj = type('Expando', (object,), {})()
    obj.foo = 71
    obj.bar = 'World'
    

    Or just, which does the same as above using dict argument:

    obj = type('Expando', (object,), {'foo': 71, 'bar': 'World'})()
    

    For Python 3, passing object to bases argument is not necessary (see type documentation).

    But for simple cases instantiation doesn't have any benefit, so is okay to do:

    ns = type('Expando', (object,), {'foo': 71, 'bar': 'World'})
    

    At the same time, personally I prefer a plain class (i.e. without instantiation) for ad-hoc test configuration cases as simplest and readable:

    class ns:
        foo = 71
        bar = 'World'
    

    Update

    In Python 3.3+ there is exactly what OP asks for, types.SimpleNamespace. It's just:

    A simple object subclass that provides attribute access to its namespace, as well as a meaningful repr.

    Unlike object, with SimpleNamespace you can add and remove attributes. If a SimpleNamespace object is initialized with keyword arguments, those are directly added to the underlying namespace.

    import types
    obj = types.SimpleNamespace()
    obj.a = 123
    print(obj.a) # 123
    print(repr(obj)) # namespace(a=123)
    

    However, in stdlib of both Python 2 and Python 3 there's argparse.Namespace, which has the same purpose:

    Simple object for storing attributes.

    Implements equality by attribute names and values, and provides a simple string representation.

    import argparse
    obj = argparse.Namespace()
    obj.a = 123
    print(obj.a) # 123 
    print(repr(obj)) # Namespace(a=123)
    

    Note that both can be initialised with keyword arguments:

    types.SimpleNamespace(a = 'foo',b = 123)
    argparse.Namespace(a = 'foo',b = 123)
    
    0 讨论(0)
  • 2021-02-02 07:39

    Just define your own class to do it:

    class Expando(object):
        pass
    
    ex = Expando()
    ex.foo = 17
    ex.bar = "Hello"
    
    0 讨论(0)
  • 2021-02-02 07:42

    One way that I found is also by creating a lambda. It can have sideeffects and comes with some properties that are not wanted. Just posting for the interest.

    dynamic_object = lambda:expando
    dynamic_object.dynamic_property_a = "abc"
    dynamic_object.dynamic_property_b = "abcdefg"
    
    0 讨论(0)
  • 2021-02-02 07:44

    Use the collections.namedtuple() class factory to create a custom class for your return value:

    from collections import namedtuple
    return namedtuple('Expando', ('dynamic_property_a', 'dynamic_property_b'))('abc', 'abcdefg')
    

    The returned value can be used both as a tuple and by attribute access:

    print retval[0]                  # prints 'abc'
    print retval.dynamic_property_b  # prints 'abcdefg'  
    
    0 讨论(0)
  • 2021-02-02 07:49

    Using an object just to hold values isn't the most Pythonic style of programming. It's common in programming languages that don't have good associative containers, but in Python, you can use use a dictionary:

    my_dict = {} # empty dict instance
    
    my_dict["foo"] = "bar"
    my_dict["num"] = 42
    

    You can also use a "dictionary literal" to define the dictionary's contents all at once:

    my_dict = {"foo":"bar", "num":42}
    

    Or, if your keys are all legal identifiers (and they will be, if you were planning on them being attribute names), you can use the dict constructor with keyword arguments as key-value pairs:

    my_dict = dict(foo="bar", num=42) # note, no quotation marks needed around keys
    

    Filling out a dictionary is in fact what Python is doing behind the scenes when you do use an object, such as in Ned Batchelder's answer. The attributes of his ex object get stored in a dictionary, ex.__dict__, which should end up being equal to an equivalent dict created directly.

    Unless attribute syntax (e.g. ex.foo) is absolutely necessary, you may as well skip the object entirely and use a dictionary directly.

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