I have a class with quite a few attributes that are provided when instanciating (init).
Looks like this, but with about 30 more attr:
class SomeObject:
def __init__(self,
first,
second):
self.first = first
self.second = second
Works fine, but gets very long an repetitive and hard to update.
Before I used a much smaller class:
class SomeObject:
def __init__(self, **attr):
self.__dict__.update(attr)
Works fine, much easier, but hard to keep track. When using an IDE (I use PyCharm) I will have no hints when writing an object and autocomplete hints are usually not working.
I would look for some mix of both classes, like:
class SomeObject:
def __init__(self,
first,
second):
self.__dict__.update(???) # I would like to do it in a single line, so I dont have to write a new attr two (three...) times.
Anybody knows if / how this is possible? Thanks a lot
Python 3.4 +
PyCharm 2016.1 CommunityEdition
... Edit / Additional ...
The problem seems to be primarily to "preserve" the code inspection in the IDE, so that the autocompletion etc. is still available on the object.
you can simply get a dictionary of the local variables with the locals builtin, then remove self and update:
class test:
def __init__(self,
a,b,c,d,e,f,g,h,i,j,k,l,m,
n,o,p,q,r,s,t,u,v,w,x,y,z):
args = locals()
del args["self"]
if "args" in args:
del args["args"]
self.__dict__.update(args)
args = list(range(26))
x = test(*args)
from string import ascii_lowercase as letters
assert args == [getattr(x,c) for c in letters]
print("worked")
Although if you really want to make your IDE happy it needs to see the explicit attribute assignment, so you could instead write a code to write the code:
def write_init_code(func):
self,*args = func.__code__.co_varnames
return "\n".join(["{0}.{1} = {1}".format(self,a)
for a in args])
class test:
def __init__(self,a,b,c,d,e,f,*v,**kw):
pass #just a moment...
print(write_init_code(test.__init__))
then just copy-paste and indent the result from write_init_code into the actual function and you are done.
The module inspect allows you to query the running program. The below example demonstrates its use:
import inspect
class Test:
def __init__(self, a, b, c, d):
argInfo = inspect.getargvalues(inspect.currentframe())
print(argInfo)
# Print argument values in order of parameters
print([ argInfo.locals[paramName] for paramName in argInfo.args[1:] ])
t = Test(1,2,3,4)
output:
ArgInfo(args=['self', 'a', 'b', 'c', 'd'], varargs=None, keywords=None, locals={'c': 3, 'self': <__main__.Test object at 0x7fb2d9c0ce10>, 'b': 2, 'd': 4, 'a': 1})
[1, 2, 3, 4]
As you can see, you can access a dictionary of the arguments and their corresponding parameter name through ArgInfo.locals.
The second print out is an example of how to put the argument values in the order of the parameter declaration.
You can use this to populate your instance variables, or you can simply copy the ArgInfo.locals dictionary.
You can use locals() to achieve what you want:
class SomeObject:
def __init__(self, first, second):
kwargs = locals()
kwargs.pop('self') # remove `self` keywork from the dict
self.__dict__.update(kwargs)
With dataclasses this is now easy and fully supported by PyCharm. By adding typing annotations and using decorator #dataclasses.dataclass we make class members act as instance members.
import dataclasses
import typing
#dataclasses.dataclass
class SomeObject:
first: str
second: str
class_member1 = None
class_member2: typing.ClassVar[str] = None
SomeObject(first="first", second="second")
Note that class fields with an annotation like first and second work like instance members. While unannotated fields like class_member1 remain as class members.
But if you want some field as a class member but still want to have typing information then use annotation typing.ClassVar. This allows you to have class_member2 which is a class member and also has typing information.
*** Made this function to automates attributes from args ***
def autoargs(self: object, locals: object) -> dict:
''' Put this line in the __init__ method of a class:
self.__dict__ = autoargs(self, locals())
to auto-attributes args to class '''
kwargs = locals
kwargs.pop('self') # remove `self` keywork from the dict
self.__dict__.update(kwargs)
return self.__dict__
class SomeObject:
def __init__(self, first, second):
# function to auto make the attributes from args (see the function doc)
self.__dict__ = autoargs(self, locals())
a = SomeObject(1,2)
print(a.first)
print(a.second)
output:
1
2
EDIT: Now that python 3.7 is out, the #dataclass class decorator solves this very elegantly.
ORIGINAL ANSWER:
With multiple cursors in PyCharm, you can easily convert
self.first = first
self.second = second
to a very long line like:
self.first, self.second = first, second
This is what I've done to keep PyCharm smart
Related
Is there a way in Python to declare instance variables from the methods arguments without the need for boilerplate writing?
For example, is there a way for self.foo, self.bar and all other arguments to be automatically declared?
def __init__(self, foo, bar, ..., last):
self.foo = foo
self.bar = bar
...
self.last = last
There actually is, though I think it's a very ugly way
class Foo:
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
Foo(name='foo', baz=123, bar=True, last=None)
As Tim Roberts suggested in the comments, using a namedtuple is a much better and cleaner way (you may also take a look at dataclasses.dataclass)
You can use a keyword argument in __init__. For example:
class MyClass:
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
Then you can create a class using whatever keyword arguments you want:
>>> mc = MyClass(foo=123, bar=456, baz=789)
>>>
Those arguments are assigned as attributes of the class instance:
>>> mc.foo
123
>>> mc.bar
456
>>> mc.baz
789
>>>
You may want to add some checks if you do this, e.g. restrict them to some known set of names. Otherwise an undesired name could override a method name for instance.
Or, you could save the keyword dictionary directly in the class instance, as a single attribute. That would insulate the class from the names. For example:
class MyClass:
def __init__(self, **kwargs):
self.args = kwargs
You could then access them through self.args.
Solution 1
As #Tim Roberts suggested this is how it can be accomplished with a namedtuple.
from collections import namedtuple
class Foo(namedtuple("Foo", ["param1", "param2"])):
pass # Rest of code goes here
my_class = Foo(1, 2)
my_class.param1 # returns 1
You can even create default arguments
namedtuple("Foo", ["param1", "param2"], defaults=[1, 2])
Solution 2
Alternatively, you can use **kwargs to set the instance variables (which doesn't use the collections library and is arguably easier) if you are fine with the user passing any random argument.
Here is an example
class Foo:
def __init__(self, **kwargs):
super().__dict__.update(kwargs)
my_class = Foo(param1=1)
my_class.param1 # returns 1
# Doesn't force you to pass the correct parameters
random_param_class = Foo(a_made_up_parameter=2)
random_param_class.param1 # Results in an error
Bear in mind that none of this is standard and most often people just declare the instance variables from within __init__.
To be honest, if you have a fixed sets of inputs, you can use this:
from inspect import getargvalues, stack
def arguments():
args = getargvalues(stack()[1][0])[-1]
del args['self']
if 'kwargs' in args:
args.update(args['kwargs'])
del args['kwargs']
return args
class myClass():
def __init__(self, foo, bar, ..., last):
# Auto update all arguments into object dictionary
self.__dict__.update(arguments())
This should do it, if you don't specify *kwargs
object = myClass(1,2,3,'foo','random')
# all the right instances will be created
# object.foo =1
# object.bar = 2
For example:
#attrs
class Foo:
a = attrib()
f = Foo(a=1, b=2)
Code above will throw an error because class Foo doesn't have b attr. But I want to discard passed b value as if I just called f = Foo(a=1). In my use case I have dynamic dict (which I want to transform into attr-class) and I simply do not need some of the keys.
I think I figured out a more elegant solution which allows you to take advantage of the features of attrs while also tweaking the __init__ logic. See attrs documentation for more info.
#attr.s(auto_attribs=True, auto_detect=True)
class Foo():
a: int
optional: int = 3
def __init__(self,**kwargs):
filtered = {
attribute.name: kwargs[attribute.name]
for attribute in self.__attrs_attrs__
if attribute.name in kwargs
}
self.__attrs_init__(**filtered)
The code above allows you to specify extraneous keyword args. It also allows for optional args.
>>> Foo(a = 1, b = 2)
Foo(a=1, optional=3)
attrs detects the explicit init method (due to auto_detect=True) and still creates the init function, but calls it __attrs_init__. This allows you do define your own init function to do preprocessing and then call __attrs_init__ when you are done.
>>> import inspect
>>> print(inspect.getsource(Foo.__attrs_init__))
def __attrs_init__(self, a, optional=attr_dict['optional'].default):
self.a = a
self.optional = optional
class FromDictMixin:
#classmethod
def from_dict(cls, data: dict):
return cls(**{
a.name: data[a.name]
for a in cls.__attrs_attrs__
})
#attrs
class Foo(FromDictMixin):
a = attrib()
It works, but it looks kinda ugly. I was hopping that attrs lib had out of the box solution.
This seems to be more of a question of serialization/deserialization/validation and attrs is quite strict on its argument for multiple reasons. One of them is typing (as in types, not pressing keys :)) and the other is robustness/debugabiity. Ignoring arguments that you might have just misspelt can lead to very frustrating moments. It's better to move this kind of stuff into a separate layer.
You can find some possible tools for that in https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs.
I had to do something similar but I didn't want to write a custom __init__ method for every class. So I created a decorator where it would attach an __init__ method to the class before instantiation then wrap in attrs.define decorator.
This is just an example but does what you want.
import attrs
def define(cls):
def __init__(cls, **kwargs):
filtered = {}
for attr in cls.__attrs_attrs__:
if attr.name in kwargs:
filtered[attr.name] = kwargs[attr.name]
cls.__attrs_init__(**filtered)
def wrapper(*args, **kwargs):
nonlocal cls
cls.__init__ = __init__
cls = attrs.define(cls)
return cls(*args, **kwargs)
return wrapper
#define
class Booking:
id: int
id_hash: str
booking = {"id": 1, "id_hash": "a3H33lk", "foo": "bar"}
b = Booking(**booking)
print(b)
# Booking(id=1, id_hash='a3H33lk')
I have the following problem and I need advice on how to solve it the best technically in Python. As I am new to programming I would like to have some advice.
So I will have the following object and they should store something. Here is an example:
object 1: cash dividends (they will have the following properties)
exdate (will store a list of dates)
recorddate (will store a list of dates)
paydate (will store a list of dates)
ISIN (will store a list of text)
object 2: stocksplits (they will have the following prpoerties)
stockplitratio (will be some ration)
exdate(list of dates)
...
I have tried to solve it like this:
class cashDividends(object):
def __init__(self, _gross,_net,_ISIN, _paydate, _exdate, _recorddate, _frequency, _type, _announceddate, _currency):
self.gross = _gross
self.net = _net
self.ISIN = _ISIN
self.paydate = _paydate
self.exdate = _exdate
self.recorddate = _recorddate
self.frequency = _frequency
self.type = _type
self.announceddate = _announceddate
self.currency = _currency
So if I have this I would have to create another class named stockplits and then define an __init__ function again.
However is there a way where I can have one class like "Corporate Actions" and then have stock splits and cashdividends in there ?
Sure you can! In python you can pass classes to other classes.
Here a simple example:
class A():
def __init__(self):
self.x = 0
class B():
def __init__(self):
self.x = 1
class Container():
def __init__(self, objects):
self.x = [obj.x for obj in objects]
a = A()
b = B()
c = Container([a,b])
c.x
[0,1]
If I understood correctly what you want is an object that has other objects from a class you created as property?
class CorporateActions(object):
def __init__(self, aCashDividend, aStockSplit):
self.cashDividend = aCashDividend
self.stockSplit = aStockSplit
myCashDividends = CashDividends(...) #corresponding parameters here
myStockSplit = StockSplit(...)
myCorporateActions = CorporateActions(myCashDividends, myStockSplit)
Strictly speaking this answer isn't an answer for the final question. However, it is a way to make your life slightly easier.
Consider creating a sort-of template class (I'm using this term loosely; there's no such thing in Python) that does the __init__ work for you. Like this:
class KwargAttrs():
def __init__(self, **kwargs):
for k,v in kwargs.items():
setattr(self, k, v)
def _update(self, **kwargs):
args_dict = {k:(kwargs[k] if k in kwargs else self.__dict__[k]) for k in self.__dict__}
self.__dict__.update(args_dict)
This class uses every supplied keyword argument as an object attribute. Use it this way:
class CashDividends(KwargAttrs):
def __init__(self, gross, net, ISIN, paydate, exdate, recorddate, frequency, type, announceddate, currency):
# save the namespace before it gets polluted
super().__init__(**locals())
# work that might pollute local namespace goes here
# OPTIONAL: update the argument values in case they were modified:
super()._update(**locals())
Using a method like this, you don't have to go through the argument list and assign every single object attribute; it happens automatically.
We bookend everything you need to accomplish in the __init__ method with method calls to the parent-class via super(). We do this because locals() returns a dict every variable in the function's current namespace, so you need to 1.) capture that namespace before any other work pollutes it and 2.) update the namespace in case any work changes the argument values.
The call to update is optional, but the values of the supplied arguments will not be updated if something is done to them after the call to super().__init__() (that is, unless you change the values using setattr(self, 'argname, value)`, which is not a bad idea).
You can continue using this class like so:
class StockSplits(KwargAttrs):
def __init__(self, stocksplitratio, gross, net, ISIN, paydate, exdate, recorddate, frequency, type, announceddate, currency):
super().__init__(**locals())
As mentioned in the other answers you can create a container for our other classes, but you can even do that using this same template class:
class CorporateActions(KwargAttrs):
def __init__(self, stock_splits , cash_dividends):
super().__init__(**locals())
ca = CorporateActions(stock_splits = StockSplits(<arguments>), cash_dividends = CashDividends(<arguments>) )
I have class:
class A(object):
def do_computing(self):
print "do_computing"
Then I have:
new_class = type('B', (object,), {'a': '#A', 'b': '#B'})
What I want to achieve is to make all methods and properties on class A a member of class B. Class A can have from 0 to N such elements. I want to make them all a member of class B.
So far I get to:
methods = {}
for el in dir(A):
if el.startswith('_'):
continue
tmp = getattr(A, el)
if isinstance(tmp, property):
methods[el] = tmp
if isinstance(tmp, types.MethodType):
methods[el] = tmp
instance_class = type('B', (object,), {'a': '#A', 'b': '#B'})
for name, func in methods.items():
new_method = types.MethodType(func, None, instance_class)
setattr(instance_class, name, new_method)
But then when I run:
instance().do_computing()
I get an error:
TypeError: unbound method do_computing() must be called with A instance as first argument (got B instance instead)
Why I had to do that? We have a lot of legacy code and I need fancy objects that will pretend they are old objects but really.
One more important thing. I cannot use inheritance, to much magic happens in the background.
If you do it like this, it will work:
import types
class A(object):
def do_computing(self):
print "do_computing"
methods = {name:value for name, value in A.__dict__.iteritems()
if not name.startswith('_')}
instance_class = type('B', (object,), {'a': '#A', 'b': '#B'})
for name, func in methods.iteritems():
new_method = types.MethodType(func, None, instance_class)
setattr(instance_class, name, new_method)
instance_class().do_computing()
Unless I'm missing something, you can do this with inheritance:
class B(A):
def __init__(self):
super(B, self).__init__()
Then:
>>> b = B()
>>> b.do_computing()
do_computing
Edit: cms_mgr said the same in the comments, also fixed indentation
are you creating a facade? maybe you want something like this:
Making a facade in Python 2.5
http://en.wikipedia.org/wiki/Facade_pattern
you could also use delegators. here's an example from the wxpython AGW:
_methods = ["GetIndent", "SetIndent", "GetSpacing", "SetSpacing", "GetImageList", "GetStateImageList",
"GetButtonsImageList", "AssignImageList", "AssignStateImageList", "AssignButtonsImageList",
"SetImageList", "SetButtonsImageList", "SetStateImageList", 'other_methods']
def create_delegator_for(method):
"""
Creates a method that forwards calls to `self._main_win` (an instance of :class:`TreeListMainWindow`).
:param `method`: one method inside the :class:`TreeListMainWindow` local scope.
"""
def delegate(self, *args, **kwargs):
return getattr(self._main_win, method)(*args, **kwargs)
return delegate
# Create methods that delegate to self._main_win. This approach allows for
# overriding these methods in possible subclasses of HyperTreeList
for method in _methods:
setattr(HyperTreeList, method, create_delegator_for(method))
Note that these wrap class methods... i.e both functions take a signature like def func(self, some, other, args) and are intended to be called like self.func(some, args). If you want to delegate a class function to a non-class function, you'll need to modify the delegator.
You can inherit from a parent class as such:
class Awesome():
def method_a():
return "blee"
class Beauty(Awesome):
def __init__(self):
self.x = self.method_a()
b = Beauty()
print(b.x)
>>> "blee"
This was freely typed, but the logic is the same none the less and should work.
You can also do fun things with setattr like so:
#as you can see this class is worthless and is nothing
class blee():
pass
b = blee()
setattr(b, "variable_1", "123456")
print(b.variable_1)
>>> 123456
essentially you can assign any object, method to a class instance with setattr.
EDIT: Just realized that you did use setattr, woops ;)
Hope this helps!
I'm currently creating an object like this:
class Obj(object):
def __init__(self,**kwargs):
params = ['val1','val2','val3','val4',...]
for p in params:
setattr(self,p,kwargs.get(p,None))
I'm doing this so I don't have to do this:
class Obj(object):
def __init__(self,val1=None,val2=None,val3=None,val4=None,...):
self.val1=val1
self.val2=val2
self.val3=val3
self.val4=val4
...
My question is, can you do a mix of the two? Where I can define the expected parameters yet still loop the parameters to set the attributes? I like the idea of defining the expected parameters because it is self documenting and other developers don't have to search for what kwargs are used.
I know it seems pretty petty but I'm creating an object from some XML so I'll be passing in many parameters, it just clutters the code and bugs me.
I did google this but couldn't find anything, probably because dictionary and kwargs together point to kwarg examples.
UPDATE: To be more specific, is it possible to get a dictionary of passed in parameters so I don't have to use kwargs at all?
Sudo code:
class Obj(object):
def __init__(self,val1=None,val2=None,val3=None,val4=None,...):
for k,v in dictionary_of_paramters.iteritems():
setattr(self,k,v)
You can use the inspect module:
import inspect
def myargs(val1, val2, val3=None, val4=5):
print inspect.currentframe().f_locals
it shows all the locals available on the current stack frame.
myargs('a','b')
==> {'val3': None, 'val2': 'b', 'val1': 'a', 'val4': 5}
(note: it's not guaranteed to be implemented on all Python interpreters)
edit: i concur that it's not a pretty solution. what i would do is more like:
def _yourargs(*names):
"returns a dict with your named local vars"
alllocs = inspect.stack()[1][0].f_locals
return {n:alllocs[n] for n in names}
def askformine(val1, val2, val3=None, val4=5):
"example to show just those args i'm interested in"
print _yourargs('val1','val2','val3','val4')
class Obj(object):
"example inserting some named args as instance attributes"
def __init__(self, arg1, arg2=4):
self.__dict__.update(_yourargs('arg1','arg2'))
edit2 slightly better:
def pickdict(d,*names):
"picks some values from a dict"
return {n:d[n] for n in names}
class Obj(object):
"example inserting some named args as instance attributes"
def __init__(self, arg1, arg2=4):
self.__dict__.update(pickdict(locals(),'arg1','arg2'))
There is no nice way to get a dictionary of all the arguments to a function. The **kwargs syntax only collects up the extra keyword arguments, not the ones that match explicit parameters in the function definition.
Although you won't be able to get the parameters without using kwargs or the inspect module (see other answers), you can do something like this...
class Obj(object):
def __init__(self, **kwargs):
self.__dict__.update(**kwargs)
Every object has a dictionary that stores all of the attributes, which you can access via self.__dict__. Then you're just using update to set all of the attributes in that object's internal dictionary.
See this question on some discussion of this method.
If you want to obtain the args dict at the very top of your method, before you define any locals, this is as simple as:
class Obj(object):
def __init__(self,val1=None,val2=None,val3=None,val4=None):
kwargs = dict(locals())
To read this dict later on, some introspection magic is required:
class Obj(object):
def __init__(self,val1=None,val2=None,val3=None,val4=None):
# feel free to add more locals
loc = dict(locals())
fun = sys._getframe().f_code
kwargs = {x:loc[x] for x in fun.co_varnames[:fun.co_argcount]}
You can also make the latter reusable by adding a function like this:
def getargs():
f = sys._getframe(1)
return {x:f.f_locals[x] for x in f.f_code.co_varnames[:f.f_code.co_argcount]}
and then:
class Obj(object):
def __init__(self,val1=None,val2=None,val3=None,val4=None):
# feel free to add more locals
kwargs = getargs()
This is cpython-specific, I guess.
Yes you can mix the two.
See below:
def method(a, b=1, *args, **kwargs):
'''some code'''
This is valid. Here:
'a' is a required argument
'b' is a default argument
'args' will have all the non-keyword arguments and
'kwargs' will have all the keyword arguments.
Example:
method(1,2,3,4,5,test=6,test1=7)
This call will have:
a=1
b=2
args=(3,4,5)
kwargs={'test':6,'test1':7}
A kind of an ugly workaround: Inject extra arguments into kwargs and use it where you want to loop over all keyword arguments (PS this is an example usage of the inspect module, but not recommended for production use):
#!/usr/bin/env python
import inspect
def inject_defaults(func):
""" injects '__defaults' key into into kwargs,
so it can be merged with kwargs in the decorated method """
args, varargs, varkwargs, defaults = inspect.getargspec(func)
have_defaults = args[-len(defaults):]
defaults_dict = dict(zip(have_defaults, defaults))
def fun(*args, **kwargs):
kwargs['__defaults'] = defaults_dict
return func(*args, **kwargs)
return fun
#inject_defaults
def f1(a,b,c, x=1, **kwargs):
kwargs.update(kwargs['__defaults'])
del kwargs['__defaults']
for k, v in kwargs.items():
# here, x, y and z will appear
print(k, v)
f1(1, 2, 3, y=3, z=2)
# prints
# ('y', 3)
# ('x', 1)
# ('z', 2)