Let's suppose that we have already defined argument groups. Now I want to get access to one of them in order to add some extra argument. So what is the best way to do this in argparse? Now my approach looks like as follows:
def get_parser(self, ...):
parser = ...
matching_groups = (g for g in parser._action_groups
if g.title == 'group name')
group = next(matching_groups, None) or parser
group.add_argument('-s', '--some-new-argument', ...)
return parser
Is there any more elegant way that allows do not access 'protected' members directly?
Related
For example, the following code gets very lengthy. I then have to pass these each into their relevant functions. The benefit here is that I can easily specify defaults all in one place (for me the class __init__).
### Parse Arguments ###
self.rui_server = self.args.get("Server", "https://url.com")
self.system_name = self.args.get("SystemName", "Generic_Accessor")
self.predicates = self.args.get("Predicates", None)
self.keywords = self.args.get("Keywords", None)
self.number_of_tests = self.args.get("NumberOfTests", None)
self.matching_lookback_count = self.args.get("MatchingLookbackCount", 10)
self.update_period = self.args.get("UpdatePeriod", 3) # in days
self.readable_logs_key = self.args.get('ReadableLogsKey', None)
VS
self.args.setdefault("Server", "https://url.com")
...etc...
Then make self.args available to every method or subclass. There is no issue with everyone having access to everything else in my implementation.
So what is actually better here and why? Or is there an even better way that I'm not aware of?
I would like to get subset of parsed arguments and send them to another function in python. I found this argument_group idea but I couldn't find to reach argument groups. This is what I want to try to do:
import argparse
def some_function(args2):
x = args2.bar_this
print(x)
def main():
parser = argparse.ArgumentParser(description='Simple example')
parser.add_argument('--name', help='Who to greet', default='World')
# Create two argument groups
foo_group = parser.add_argument_group(title='Foo options')
bar_group = parser.add_argument_group(title='Bar options')
# Add arguments to those groups
foo_group.add_argument('--bar_this')
foo_group.add_argument('--bar_that')
bar_group.add_argument('--foo_this')
bar_group.add_argument('--foo_that')
args = parser.parse_args()
# How can I get the foo_group arguments for example only ?
args2 = args.foo_group
some_function(args2)
I don't know whether a simpler solution exists, but you can create a custom "namespace" object selecting only the keys arguments you need from the parsed arguments.
args2 = argparse.Namespace(**{k: v for k, v in args._get_kwargs()
if k.startswith("foo_")})
You can customize the if clause to your needs and possibly change the argument names k, e.g. removing the foo_ prefix.
Very simple question from a Python newbie:
My understanding is that the keys in a dict are able to be just about any immutable data type. Is it possible to pass an immutable object (e.g., a member of an enum class) as a key in the **kwargs dictionary for a function or a class? I have tried it and the answer seems to be "no":
from enum import Enum
class MyEnum(Enum):
X= 'X'
Y= 'Y'
def func(*args,**kwargs):
pass
func(MyEnum.X = 1)
Output:
"SyntaxError: keyword can't be an expression"
However, there may be something I am missing.
EDIT: Note that I am not trying to make the key equal to MyEnum.X.value (which is a string in this case); I want the key to be the actual Enum object, e.g. MyEnum.X.
You're doing:
func(MyEnum.X = 1)
Here, the problem is MyEnum.X = 1 -- Your keyword (MyEnum.X) is actually an expression (getattr(MyEnum, 'X')), and expressions can't be used as keywords in function calls. In fact, only identifiers can be used as keywords.
To get your call to work, you'll need to use dictionary unpacking like this:
func(**{MyEnum.X.name: 1})
Note, to get the name of the attribute, I needed to do MyEnum.X.name or MyEnum.X.value, depending on how you set up your enum -- In your case, I they are the same thing.
>>> from enum import Enum
>>> class Foo(Enum):
... X = 'X'
...
>>> Foo.X.value
'X'
>>> Foo.X.name
'X'
This won't work, because of the way keyword arguments are being processed. The documentation says:
[...] Next, for each keyword argument, the identifier is used to determine the corresponding slot (if the identifier is the same as the first formal parameter name, the first slot is used, and so on) [...]
So there must be a way to match the key from the dictionary to the formal parameter name. The exception:
keywords must be strings
when you try to pass something that's not a string:
func(**{MyEnum.X: 1})
suggest the simplest case is required: keys must be strings.
A possible workaround is to make implicit things explicit: just create a class that contains all the necessary information you want to pass in its attributes and pass it. The code will surely be more readable.
The answer to my original question is indeed "no". However, thanks to the input from mgilson and BartoszKP and others, the following work around I came up with is not a bad solution, and solves my current problem. I offer it for others to look at who are trying to do something similar:
from enum import Enum
class MyEnum(Enum):
X= 'X'
Y= 'Y'
def func(*args,**kwargs):
#replace kwargs with kwargsNew
kwargsNew = {}
for kwkey, kwvalue in kwargs.items():
try: kwargsNew[MyEnum(kwkey)] = kwvalue
except ValueError: kwargsNew[kwkey] = kwvalue
doStuffWithKwargs(kwargsNew)
def doStuffWithKwargs(k):
for K in k:
print(K)
#Pass the name X or Y as the key;
#all other keys not found in `MyEnum` are treated normally
func(X = 1, Y = 2, Z = 3)
Output:
Z
MyEnum.X
MyEnum.Y
(no errors)
Do you actually want to create an instnace of MyEnum?
myenum = MyEnum()
func(myenum.X = 1)
One alternative I have found is to pass a dict into *args instead of **kwargs, or to assign a dict to kwargs[0] directly:
func({MyEnum.X: 1})
func(kwargs = {MyEnum.X: 1})
(No errors produced)
However, I really don't like either of these methods.
EDIT: See my second answer for a much better solution.
Excuse my poor wording in the title, but here's a longer explanation:
I have a function which as arguments takes some functions which are used to determine which data to retrieve from a database, as such:
def customer_data(customer_name, *args):
# initialize dictionary with ids
codata = dict([(data.__name__, []) for data in args])
codata['customer_observer_id'] = _customer_observer_ids(customer_name)
# add values to dictionary using function name as key
for data in args:
for coid in codata['customer_observer_id']:
codata[data.__name__].append(data(coid))
return codata
Which makes the call to the function looking something like this:
customer_data('customername', target_parts, source_group, ...)
One of these functions is defined with an extra parameter:
def polarization_value(customer_observer_id, timespan='day')
What I would like is a way to change the timespan variable in a clever way. One obvious way is to include a keyword argument in customer_observer and add an exception when the function name being called is 'polarization_value', but I have a feeling there is a better way to do this.
You can use functools.partial and pass polarization_value as :
functools.partial(polarization_value, timespan='day')
Example:
>>> import functools
def func(x, y=1):
print x, y
...
>>> new_func = functools.partial(func, y=20)
>>> new_func(100)
100 20
You may also find this helpful: Python: Why is functools.partial necessary?
So I have a database with 10 fields(call them a, b, c....j), and I need to create a function "find_use()" that can find and modify records in the database based on any combination of fields given to it as arguments ( ie: find_use(a=="happy", g =="birthday") ). Also, due to privileges, I am not generating the SQL queries directly, but instead use an SQL wrapper the system provides called "selector()" which will locate records based on the same arguments, ie: query = selector(a=="happy", g=="birthday"), which would then return a list of the matching records
So the problem is, since I don't know what arguments find_use() will be receiving, I don't know how to structure the inner call to selector(). Ideally, the call to selector should be created dynamically; something to the effect of:
def find_use(a='',b='',c='',.....j=''):
vars = locals()
for v in vars:
if v.value() != '':
q_string += '{0} == {1},'.format(v.key(),v.value())
query = selector(q_string)
...do something to query...
This, however, will not work since selector does not take a string, but rather arguments in the same format as the find_use() function. How, then, can this be done. I have the feeling this is actually really easy, and I'm just missing something obvious. Any input would be greatly appreciated.
I think you are looking for keyword argument unpacking:
def find_use(**kwargs):
query = selector(**kwargs)
...do something to query...
When defined this way, find_use can receive an arbitrary number of keyword arguments. The keyword arguments are collected in a dict, kwargs.
For example,
def foo(**kwargs):
print(kwargs)
foo(a = 'happy', g = 'birthday')
yields
{'a': 'happy', 'g': 'birthday'}
and if
def bar(a, g):
print(a)
print(g)
then
bar(**{'a': 'happy', 'g': 'birthday'}) # like selector(**kwargs)
yields
happy
birthday