How to add property to a class dynamically?

前端 未结 24 1898
梦毁少年i
梦毁少年i 2020-11-22 12:44

The goal is to create a mock class which behaves like a db resultset.

So for example, if a database query returns, using a dict expression, {\'ab\':100, \'cd\'

相关标签:
24条回答
  • 2020-11-22 13:13

    Not sure if I completely understand the question, but you can modify instance properties at runtime with the built-in __dict__ of your class:

    class C(object):
        def __init__(self, ks, vs):
            self.__dict__ = dict(zip(ks, vs))
    
    
    if __name__ == "__main__":
        ks = ['ab', 'cd']
        vs = [12, 34]
        c = C(ks, vs)
        print(c.ab) # 12
    
    0 讨论(0)
  • 2020-11-22 13:13
    class atdict(dict):
      def __init__(self, value, **kwargs):
        super().__init__(**kwargs)
        self.__dict = value
    
      def __getattr__(self, name):
        for key in self.__dict:
          if type(self.__dict[key]) is list:
            for idx, item in enumerate(self.__dict[key]):
              if type(item) is dict:
                self.__dict[key][idx] = atdict(item)
          if type(self.__dict[key]) is dict:
            self.__dict[key] = atdict(self.__dict[key])
        return self.__dict[name]
    
    
    
    d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})
    
    print(d1.a.b[0].c)
    

    And the output is:

    >> 1
    
    0 讨论(0)
  • 2020-11-22 13:14

    You can use the following code to update class attributes using a dictionary object:

    class ExampleClass():
        def __init__(self, argv):
            for key, val in argv.items():
                self.__dict__[key] = val
    
    if __name__ == '__main__':
        argv = {'intro': 'Hello World!'}
        instance = ExampleClass(argv)
        print instance.intro
    
    0 讨论(0)
  • 2020-11-22 13:16

    Here is the simple example to create property object programmatically.

    #!/usr/bin/python3
    
    class Counter:
        def __init__(self):
            cls = self.__class__
            self._count = 0
            cls.count = self.count_ref()
    
        def count_get(self):
            print(f'count_get: {self._count}')
            return self._count
    
        def count_set(self, value):
            self._count = value
            print(f'count_set: {self._count}')
    
        def count_del(self):
            print(f'count_del: {self._count}')
    
        def count_ref(self):
            cls = self.__class__
            return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)
    
    counter = Counter()
    
    counter.count
    for i in range(5):
        counter.count = i
    del counter.count
    
    '''
    output
    ======
    count_get: 0
    count_set: 0
    count_set: 1
    count_set: 2
    count_set: 3
    count_set: 4
    count_del: 4
    '''
    
    0 讨论(0)
  • 2020-11-22 13:17

    This is a little different than what OP wanted, but I rattled my brain until I got a working solution, so I'm putting here for the next guy/gal

    I needed a way to specify dynamic setters and getters.

    class X:
        def __init__(self, a=0, b=0, c=0):
            self.a = a
            self.b = b
            self.c = c
    
        @classmethod
        def _make_properties(cls, field_name, inc):
            _inc = inc
    
            def _get_properties(self):
                if not hasattr(self, '_%s_inc' % field_name):
                    setattr(self, '_%s_inc' % field_name, _inc)
                    inc = _inc
                else:
                    inc = getattr(self, '_%s_inc' % field_name)
    
                return getattr(self, field_name) + inc
    
            def _set_properties(self, value):
                setattr(self, '_%s_inc' % field_name, value)
    
            return property(_get_properties, _set_properties)
    

    I know my fields ahead of time so im going to create my properties. NOTE: you cannot do this PER instance, these properties will exist on the class!!!

    for inc, field in enumerate(['a', 'b', 'c']):
        setattr(X, '%s_summed' % field, X._make_properties(field, inc))
    

    Let's test it all now..

    x = X()
    assert x.a == 0
    assert x.b == 0
    assert x.c == 0
    
    assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
    assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
    assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2
    
    # we set the variables to something
    x.a = 1
    x.b = 2
    x.c = 3
    
    assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
    assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
    assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5
    
    # we're changing the inc now
    x.a_summed = 1 
    x.b_summed = 3 
    x.c_summed = 5
    
    assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
    assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
    assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8
    

    Is it confusing? Yes, sorry I couldn't come up with any meaningful real world examples. Also, this is not for the light hearted.

    0 讨论(0)
  • 2020-11-22 13:17

    Extending the idea from kjfletch

    # This is my humble contribution, extending the idea to serialize
    # data from and to tuples, comparison operations and allowing functions
    # as default values.
    
    def Struct(*args, **kwargs):
        FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                     types.FunctionType, types.MethodType)
        def init(self, *iargs, **ikwargs):
            """Asume that unamed args are placed in the same order than
            astuple() yields (currently alphabetic order)
            """
            kw = list(self.__slots__)
    
            # set the unnamed args
            for i in range(len(iargs)):
                k = kw.pop(0)
                setattr(self, k, iargs[i])
    
            # set the named args
            for k, v in ikwargs.items():
                setattr(self, k, v)
                kw.remove(k)
    
            # set default values
            for k in kw:
                v = kwargs[k]
                if isinstance(v, FUNCTIONS):
                    v = v()
                setattr(self, k, v)
    
        def astuple(self):
            return tuple([getattr(self, k) for k in self.__slots__])
    
        def __str__(self):
            data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
            return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))
    
        def __repr__(self):
            return str(self)
    
        def __eq__(self, other):
            return self.astuple() == other.astuple()
    
        name = kwargs.pop("__name__", "MyStruct")
        slots = list(args)
        slots.extend(kwargs.keys())
        # set non-specific default values to None
        kwargs.update(dict((k, None) for k in args))
    
        return type(name, (object,), {
            '__init__': init,
            '__slots__': tuple(slots),
            'astuple': astuple,
            '__str__': __str__,
            '__repr__': __repr__,
            '__eq__': __eq__,
        })
    
    
    Event = Struct('user', 'cmd', \
                   'arg1', 'arg2',  \
                   date=time.time, \
                   __name__='Event')
    
    aa = Event('pepe', 77)
    print(aa)
    raw = aa.astuple()
    
    bb = Event(*raw)
    print(bb)
    
    if aa == bb:
        print('Are equals')
    
    cc = Event(cmd='foo')
    print(cc)
    

    Output:

    <Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
    <Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
    Are equals
    <Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
    
    0 讨论(0)
提交回复
热议问题