How to process class **kwargs in Python? - python

How could I set object attributes using "one" class argument **kwargs?
The thing I want is to do this code in one loop:
class purchase():
def __init__(self,**kwargs):
self.set_attributes(kwargs)
def set_attributes(self,kwargs):
if 'file' in kwargs.keys():
self.file = kwargs['file']
if 'text' in kwargs.keys():
self.text = kwargs['text']
if 'language' in kwargs.keys():
self.language = kwargs['language']
It should be something like:
class purchase():
def __init__(self,**kwargs):
self.set_attributes(kwargs)
def set_attributes(self,kwargs):
for v,k in kwargs.iteritems():
self.k = v
which of course does not work.
Is there a way how to programatically set object attributes in one loop?

Here you go
class purchase():
def __init__(self,**kwargs):
self.set_attributes(kwargs)
def set_attributes(self,kwargs):
for k, v in kwargs.iteritems():
setattr(self, k, v)

Related

How to list all staticmethods of a python class

I have a class somewhat like following:
from joblib import Memory
import time
def find_static_methods(cls):
# to be implemented
pass
class A:
def __init__(self, cache_path: str):
self._memory = Memory(cache_path, verbose=0)
self._methods = {}
for name, method in find_static_methods(A):
self._methods[name] = self._memory.cache(method)
def __getattribute__(self, item):
if item in self._methods:
return self._methods[item]
return super(A, self).__getattribute__(item)
#staticmethod
def method1(a: int, b: int):
time.sleep(3)
return a + b
I'm trying to memoize method1 using joblib.Memory. But I don't know the cache_path in advance. Please help me with the implementation of find_static_methods here.
Here is another way:
A_attrs = A.__dict__
for k, v in A_attrs.items():
if isinstance(v, staticmethod):
print(k)

Add attributes to object by init kwargs

class Obj:
def __init__(self, **kw):
# code
obj = Obj(a=5, b=10)
print(obj.a, obj.b) # 5 10
Is there a proven solution to this task?
You can do the following to assign attributes passed in **kw:
class Obj:
def __init__(self, **kw):
# in case of python 2, the following line is: for k, v in kw.iteritems():
for k, v in kw.items():
setattr(self, k, v)
and then use the way you mentioned in your post:
obj = Obj(a=5, b=10)
print(obj.a, obj.b) # 5 10

How do I define setter, getter for dynamically added attributes

I have a class as follows:
class A:
def __init__(self):
pass
def add_attr(self, name):
setattr(self, name, 'something')
How do I define custom setter, getter for self.name? I cannot use __setattr__, __getattribute__ because that will change the behaviour of add_attr too.
EDIT: the users of this class will add arbitrary number of attributes with arbitrary names:
a = A()
a.add_attr('attr1')
a.add_attr('attr2')
I want custom behavior for only these user added attributes.
Building off #Devesh Kumar Singh’s answer, I would implement it in some way like this:
class A:
def __init__(self):
self.attrs = {}
def __setattr__(self, key, value):
if key in self.attrs:
self.set_attr(key, value)
else:
object.__setattr__(self, key, value)
def __getattribute__(self, key):
if key in self.__dict__.get(attrs, {}):
return self.__dict__['get_attr'](self, key)
return object.__getattribute__(self, key)
def get_attr(self, key):
r = self.attrs[key]
# logic
return r
def set_attr(self, key, value):
# logic
self.attrs[key] = value
def add_attr(self, key, value=None):
self.attrs[key] = value
add_attr is only used to initialise the variable the first time. You could also edit __setattr__ to set all new attributes in the self.attrs rather than self.__dict__
Custom getter and setter logic? That's what a property is made for. Usually these are used to magically mask function calls and make them look like attribute access
class MyDoubler(object):
def __init__(self, x):
self._x = x
#property
def x(self):
return x * 2
#x.setter
def x(self, value):
self._x = value
>>> md = MyDoubler(10)
>>> md.x
20
>>> md.x = 20
>>> md.x
40
>>> md._x
20
But there's no rule saying you can't abuse that power to add custom behavior to your getters and setters.
class A(object):
def __init__(self):
pass
#staticmethod
def default_getter_factory(name):
def default_getter(self):
return self.name
return default_getter
#staticmethod
def default_setter_factory(name):
def default_setter(self, value):
setattr(self, name, value)
return default_setter
def add_attr(self, name, getterfactory=None, setterfactory=None):
private_name = f"_{name}"
if getterfactory is None:
getterfactory = self.__class__.default_getter_factory
if setterfactory is None:
setterfactory = self.__class__.default_setter_factory
getter, setter = getterfactory(private_name), setterfactory(private_name)
getter = property(getter)
setattr(self.__class__, name, getter)
setattr(self.__class__, name, getter.setter(setter))
That said this is all a bit silly, and chances are that whatever it is you're trying to do is a thing that shouldn't be done. Dynamic programming is all well and good, but if I were to review code that did this, I would think very long and hard about alternative solutions before approving it. This reeks of technical debt to me.
One possibility I could think of is to have a dictionary of dynamic attributes, and set and get the dynamic attributes using the dictionary
class A:
def __init__(self):
#Dictionary of attributes
self.attrs = {}
#Set attribute
def set_attr(self, name):
self.attrs[name] = 'something'
#Get attribute
def get_attr(self, name):
return self.attrs.get(name)
a = A()
a.set_attr('var')
print(a.get_attr('var'))
The output will be something
Or an alternate is to use property decorator to add arguments explicitly outside the class, as described here
class A:
def __init__(self):
pass
a = A()
#Add attributes via property decorator
a.attr_1 = property(lambda self: self.attr_1)
a.attr_2 = property(lambda self: self.attr_2)
#Assign them values and print them
a.attr_1 = 4
a.attr_2 = 6
print(a.attr_1, a.attr_2)
The output will be 4 6
I am gonna answer my own question just for reference. This is based on others' answers here. The idea is to use default __setattr__ and __getattribute__ on attributes not added through add_attr.
class A:
def __init__(self):
self.attrs = {}
def add_attr(self, name):
self.attrs[name] = 'something'
def __getattribute__(self, name):
try:
object.__getattribute__(self, 'attrs')[name] # valid only if added by user
# custom logic and return
except (KeyError, AttributeError):
return object.__getattribute__(self, name)
def __setattr__(self, name, val):
# similar to __getattribute__

Passing kwargs in a class inheritance chain

I have the following setup:
class A:
def __init__(self, **kwargs):
# Some variables initialized
for k, v in kwargs.items():
setattr(self, k, v)
class B(A):
def __init__(self, **kwargs):
A.__init__(self, **kwargs)
self._b = {}
for k, v in kwargs.items():
setattr(self, k, v)
#property
def b(self):
return self._b
#b.setter
def b(self, value):
self._b.update(value)
class C(B):
def __init__(self, **kwargs):
B.__init__(self, **kwargs)
# Some variables initialized
for k, v in kwargs.items():
setattr(self, k, v)
When I now create a new instance of C I get the following error:
AttributeError: 'C' object has no attribute '_b'
Now this makes sense since B._b hasn't been initialized when A.__init__(self, **kwargs) is being called. I can resolve this issue simply by re-ordering the B's initialization like so,
class B(A):
def __init__(self, **kwargs):
self._b = {}
A.__init__(self, **kwargs)
for k, v in kwargs.items():
setattr(self, k, v)
I'd like to understand if there is a recommended/best practice approach when I need to pass kwargs from child to parent classes during initialization? It seems to me like the following things would work,
Re-order the initialization like I have above
Assign kwargs in each child class then pop them and pass the remaining kwargs along to the parent initialization
Something better
Hoping to get some approaches for 3.
The issue you have is with these loops:
for k, v in kwargs.items():
setattr(self, k, v)
You have one in each class, and that means that every one of the classes is setting all the keyword arguments as attributes on self.
When that loop runs in A, it fails because B has a property that needs initializing before it can work.
As you noted in the question, a quick fix would be to make sure that B sets up its dictionary before it runs A.__init__:
class B(A):
def __init__(self, **kwargs):
_b = {} # set this up first
A.__init__(self, **kwargs) # before calling the superclass
for k, v in kwargs.items():
setattr(self, k, v)
But there's probably a better approach that would let you avoid the redundant loops. I'd suggest explicitly naming the keyword arguments you expect in each class. That way b will only be seen by the B class, not by A, nor C (except as part of kwargs).
class A:
def __init__(self, *, a): # a is keyword-only arg, no kwargs accepted here
self.a = a
class B(A):
def __init__(self, *, b, **kwargs):
super().__init__(**kwargs) # doesn't mess with b!
self._b = {}
self.b = b
#property
def b(self):
...
class C(B):
def __init__(self, *, c, **kwargs):
super().__init__(**kwargs)
self.c = c
Now you can call C(a="foo", b={1: 2}, c="bar") and each class will only pay attention to the attribute it cares about.

How do I copy **kwargs to self?

Is there a way that I can define __init__ so keywords defined in **kwargs are assigned to the class?
For example, if I were to initialize a ValidationRule class with ValidationRule(other='email'), the value for self.other should be added to the class without having to explicitly name every possible kwarg.
class ValidationRule:
def __init__(self, **kwargs):
# code to assign **kwargs to .self
I think somewhere on the stackoverflow I've seen such solution. Anyway it can look like:
class ValidationRule:
__allowed = ("other", "same", "different")
def __init__(self, **kwargs):
for k, v in kwargs.iteritems():
assert( k in self.__class__.__allowed )
setattr(self, k, v)
This class will only accept arguments with a whitelisted attribute names listed in __allowed.
This may not be the cleanest way, but it works:
class ValidationRule:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
I think I prefer ony's solution because it restricts available properties to keep you out of trouble when your input comes from external sources.
You could do something like this:
class ValidationRule:
def __init__(self, **kwargs):
for (k, v) in kwargs.items():
setattr(self, k, v)
class ValidationRule:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
You can set your kwargs arguments by updating __dict__ attribute of the instance.
class ValidationRule:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
This could be considered nicer than updating __dict__:
class C:
def __init__(self, **kwargs):
vars(self).update(kwargs)
>>> c = C(a='a', b='b')
>>> c.a # returns 'a'
>>> c.b # returns 'b'
I found the above answers helpful and then refined:
class MyObj(object):
def __init__(self, key1=1, key2=2, key3=3):
for (k, v) in locals().iteritems():
if k != 'self':
setattr(self, k, v)
Test:
>>> myobj = MyObj(key1=0)
>>> print myobj.key1
0
And validation is also there:
>>> myobj = MyObj(key4=4)
TypeError: __init__() got an unexpected keyword argument 'key4'

Categories

Resources