How to avoid repetition in a ternary operator assignment? - python

When fetching a number of config values from os.environ, it's nice to have defaults in the python code to easily allow the application to start in a number of contexts.
A typical django settings.py has a number of
SOME_SETTING = os.environ.get('SOME_SETTING')
lines.
To provide sensible defaults we opted for
SOME_SETTING = os.environ.get('SOME_SETTING') or "theValue"
However, this is error prone because calling the application with
SOME_SETTING=""
manage.py
will lead SOME_SETTING to be set to theValue instead of the explicitly defined ""
Is there a way to assign values in python using the ternary a = b if b else d without repeating b or assigning it to a shorthand variable before?
this becomes obvious if we look at
SOME_VERY_LONG_VAR_NAME = os.environ.get('SOME_VERY_LONG_VAR_NAME') if os.environ.get('SOME_VERY_LONG_VAR_NAME') else 'meh'
It would be much nicer to be able to do something like
SOME_VERY_LONG_VAR_NAME = if os.environ.get('SOME_VERY_LONG_VAR_NAME') else 'meh'

Just like Python's built-in mapping class dict, os.environ.get has a second argument, and it seems like you want it:
SOME_SETTING = os.environ.get('SOME_SETTING', "theValue")
This is the same as
try:
SOME_SETTING = os.environ['SOME_SETTING']
except KeyError:
SOME_SETTING = "theValue"

If you read dict.get()'s doc, you'll find out the method's signature is get(self, key, default=None). The default argument is what gets returned if the key is not found in the dict (and default to a sensible None). So you can use this second argument instead of doing an erroneous boolean test:
SOME_SETTING = os.environ.get('SOME_SETTING', "theValue")

Related

How to pass a parameter as a default?

I want to use the default of a parameter, but include its name in the call. I thought that setting the parameter to None would do that, but it doesn't.
For example:
def a(num=3):
print(num)
a(num=None) #returns "None", not "3" like I want it to.
How can I use the default of a named parameter while including it in the call? (Is it even possible?)
Just to explain why I would want to do something like this (since maybe it's a problem in my overall coding style):
I often times have code like this
def add(num, numToAdd=1):
return num+numToAdd
def addTwice(num, numToAdd=None):
for i in range(2):
num=add(num, numToAdd=numToAdd)
return num
addTwice(3) #throws an error instead of returning 5
What I want is for addTwice's numToAdd to always use the default of add's numToAdd, no matter what it is.
The reason: maybe later in the code I realize that it's better to add 2 as the default when executing add than it is to add 1.
So I change it to
def add(num, numToAdd=2):
return num+numToAdd
But, this won't help anything unless I can always specify in addTwice to use the default if it receives a default.
So, that's the rationale.
In other words: I'm having a function (the first function) call another function (the second function), and if the first function has to use a default value, I want it to default to being the default value on the second function. That way, I only have to change the default value on the second function (not every single function that calls it as well) in order to change the default functionality.
There's no built-in support to explicitly use the default value of a parameter. If you don't want to leave the parameter out at the call site, you'd have to retrieve the default value very manually:
import inspect
a(num=inspect.signature(a).parameters['num'].default)
which wouldn't be worth it in most cases.
def a(num=None):
if num is None:
num = 3
print(num)
a(num=None) # prints 3
a() # prints 3
... I guess ... maybe
alternatively to explain default params
def a(num=3):
print(num)
a(num=None) # prints None
a() # prints 3
No, you can't: that's a contradiction in terms. The default value is used if and only if you do not supply a value in the arguments list. If you supply a value, then the default cannot be used within the routine.
There is a great answer on how to do this (if you decide that the default-getting functionality I asked for is really what you want). But, I just wanted to point out that in practice I believe what I was trying to achieve is normally done with global variables.
That is, the usual way to do what I wanted to do is:
DEFAULT_NUM_TO_ADD = 1
def add(num, numToAdd=DEFAULT_NUM_TO_ADD):
return num+numToAdd
def addTwice(num, numToAdd=DEFAULT_NUM_TO_ADD):
for i in range(2):
num=add(num, numToAdd=numToAdd)
return num
addTwice(3) # returns 5
This allows me to quickly change the default, and the same default is used for both functions. It's explicit and very clear; it's pythonic.

How to write super long dictionaries cleaner in function arguments?

I am using Argh in Python 3.6 to create a complex command-line function, but because of my deep configuration file, getting a default value for an argument in the function takes a long string of dictionary keys.
This does not look particularly readable because there is a dictionary value as a key of another dictionary. It could get even more nested than
this.
There can be more arguments with default values like this, so keeping this up would get even more confusing soon. This is and example with just one default argument:
import argh
import config
#arg('-v', '--version')
def generate(
kind,
version=config.template[config.data['default']['template']]['default']['version']):
return ['RETURN.', kind, version]
The version argument default value is retrieved from my config module that generates a lot of data in list and dictionary formats.
To try and better explain the default value:
config.template[ # dictionary containing variables for a particular template
config.data['default']['template'] # the default template name set in the main configuration
]['default']['version'] # The default version variable within that particular template
What do you recommend to keep this more readable?
I'd just use the same trick used for mutable default values. This gives you more room to write something more readable.
#arg('-v', '--version')
def generate(kind, version=None):
if version is None:
d = config.data['default']['template']
version = config.template[d]['default']['version']
return ['RETURN.', kind, version]
One drawback is that this is techinically different, as the data in config.data (or any of the dicts) could change between when the function is defined and when it is run. You can do the dict lookups once before the function is defined to mitigate that.
# Choose whatever refactoring looks good to you
default_template = config.data['default']['template']
default_version = config.template[default_template]['default']['version']
#arg('-v', '--version')
def generate(kind, version=default_version):
return ['RETURN.', kind, version]
del default_template default_version # Optional
Why do it on one line:
default_template_id = config.data['default']['template']
default_template = config.template[default_template_id]
default_version = default_template['default']['version']
def generate(kind, version=default_version):
return ['RETURN.', kind, version]

Avoid extra line for attribute check?

I am developing this Python project where I encounter a situation many times and I wondered if there is a better way.
There is a list of class instances. Some part of lists are empty(filled with None).
Here is an example list.
ins_list = [ins_1, ins_2, None, ins_3, None]
I have to do some confirmations throughout the program flow. There are points where I need the control an attribute of these instances. But only indexes are given for choosing an instance from the list and it may be one of the empty elements. Which would give an error when the attribute is called. Here is an example program flow.
ind = 2
if ins_list[ind].some_attribute == "thing":
# This would give error when empty element is selected.
I deal with this by using,
if ins_list[ind]:
if ins_list[ind].some_attribute == "thing":
# This works
I am okay with using this. However the program is a long one, I apply this hundreds of times. Is there an easier, better way of doing this, it means I am producing reduntant code and increasing indentation level for no reason. I wish to know if there is such a solution.
Use a boolean operator and.
if ins_list[ind] and ins_list[ind].some_attribute == "thing":
# Code
As coder proposed, you can remove None from your list, or use dictionaries instead, to avoid to have to create an entry for each index.
I want to propose another way: you can create a dummyclass and replace None by it. This way there will be no error if you set an attribute:
class dummy:
def __nonzero__(self):
return False
def __setattr__(self, k, v):
return
mydummy = dummy()
mylist = [ins_1, ins_2, mydummy, ins_3, mydummy]
nothing will be stored to the dummyinstances when setting an attribute
edit:
If the content of the original list cannot be chosen, then this class could help:
class PickyList(list):
def __init__(self, iterable, dummyval):
self.dummy = dummyval
return super(PickyList, self).__init__(iterable)
def __getitem__(self, k):
v = super(PickyList, self).__getitem__(k)
return (self.dummy if v is None else v)
mylist = PickyList(ins_list, mydummy)
There are these two options:
Using a dictionary:
Another way would be to use a dictionary instead. So you could create your dictionary once the list is filled up with elements. The dictionary's keys would be the values of your list and as values you could use the attributes of the elements that are not None and "No_attr" for those that are None. (Note: Have in mind that python dictionaries don't support duplicate keys and that's why I propose below to store as keys your list indexes else you will have to find a way to make keys be different)
For example for a list like:
l = [item1,item2,None,item4]
You could create a dictionary:
d = {item1:"thing1", item2:"thing2", None:"No_attr", item3:"thing3"}
So in this way every time you would need to make a check, you wouldn't have to check two conditions, but you could check only the value, such as:
if d.values()[your_index]=="thing":
The only cons of this method is that standard python dictionaries are inherently unordered, which makes accessing dictionary values by index a bit dangerous sometimes - you have to be careful not to change the form-arrangement of the dictionary.
Now, if you want to make sure that the index stays stable, then you would have to store it some way, for example select as keys of your dictionary the indexes, as you will have already stored the attributes of the items - But that is something that you will have to decide and depends strongly on the architecture of your project.
Using a list:
In using lists way I don't think there is a way to avoid your if statement - and is not bad actually. Maybe use an and operator as it is mentioned already in another answer but I don't think that makes any difference anyway.
Also, if you want to use your first approach:
if ins_list[ind].some_attribute == "thing":
You could try using and exception catcher like this:
try:
if ins_list[ind].some_attribute == "thing":
#do something
except:
#an error occured
pass
In this case I would use an try-except statement because of EAFP (easier to ask for forgivness than permission). It won't shorten yout code but it's a more Pythonic way to code when checking for valid attributes. This way you won't break against DRY (Don't Repat Yourself) either.
try:
if ins_list[ind].some_attribute == "thing":
# do_something()
except AttributeError:
# do_something_else()

Too many if statements

I have some topic to discuss. I have a fragment of code with 24 ifs/elifs. Operation is my own class that represents functionality similar to Enum. Here is a fragment of code:
if operation == Operation.START:
strategy = strategy_objects.StartObject()
elif operation == Operation.STOP:
strategy = strategy_objects.StopObject()
elif operation == Operation.STATUS:
strategy = strategy_objects.StatusObject()
(...)
I have concerns from readability point of view. Is is better to change it into 24 classes and use polymorphism? I am not convinced that it will make my code maintainable... From one hand those ifs are pretty clear and it shouldn't be hard to follow, on the other hand there are too many ifs.
My question is rather general, however I'm writing code in Python so I cannot use constructions like switch.
What do you think?
UPDATE:
One important thing is that StartObject(), StopObject() and StatusObject() are constructors and I wanted to assign an object to strategy reference.
You could possibly use a dictionary. Dictionaries store references, which means functions are perfectly viable to use, like so:
operationFuncs = {
Operation.START: strategy_objects.StartObject
Operation.STOP: strategy_objects.StopObject
Operation.STATUS: strategy_objects.StatusObject
(...)
}
It's good to have a default operation just in case, so when you run it use a try except and handle the exception (ie. the equivalent of your else clause)
try:
strategy = operationFuncs[operation]()
except KeyError:
strategy = strategy_objects.DefaultObject()
Alternatively use a dictionary's get method, which allows you to specify a default if the key you provide isn't found.
strategy = operationFuncs.get(operation(), DefaultObject())
Note that you don't include the parentheses when storing them in the dictionary, you just use them when calling your dictionary. Also this requires that Operation.START be hashable, but that should be the case since you described it as a class similar to an ENUM.
Python's equivalent to a switch statement is to use a dictionary. Essentially you can store the keys like you would the cases and the values are what would be called for that particular case. Because functions are objects in Python you can store those as the dictionary values:
operation_dispatcher = {
Operation.START: strategy_objects.StartObject,
Operation.STOP: strategy_objects.StopObject,
}
Which can then be used as follows:
try:
strategy = operation_dispatcher[operation] #fetch the strategy
except KeyError:
strategy = default #this deals with the else-case (if you have one)
strategy() #call if needed
Or more concisely:
strategy = operation_dispatcher.get(operation, default)
strategy() #call if needed
This can potentially scale a lot better than having a mess of if-else statements. Note that if you don't have an else case to deal with you can just use the dictionary directly with operation_dispatcher[operation].
You could try something like this.
For instance:
def chooseStrategy(op):
return {
Operation.START: strategy_objects.StartObject
Operation.STOP: strategy_objects.StopObject
}.get(op, strategy_objects.DefaultValue)
Call it like this
strategy = chooseStrategy(operation)()
This method has the benefit of providing a default value (like a final else statement). Of course, if you only need to use this decision logic in one place in your code, you can always use strategy = dictionary.get(op, default) without the function.
Starting from python 3.10
match i:
case 1:
print("First case")
case 2:
print("Second case")
case _:
print("Didn't match a case")
https://pakstech.com/blog/python-switch-case/
You can use some introspection with getattr:
strategy = getattr(strategy_objects, "%sObject" % operation.capitalize())()
Let's say the operation is "STATUS", it will be capitalized as "Status", then prepended to "Object", giving "StatusObject". The StatusObject method will then be called on the strategy_objects, failing catastrophically if this attribute doesn't exist, or if it's not callable. :) (I.e. add error handling.)
The dictionary solution is probably more flexible though.
If the Operation.START, etc are hashable, you can use dictionary with keys as the condition and the values as the functions to call, example -
d = {Operation.START: strategy_objects.StartObject ,
Operation.STOP: strategy_objects.StopObject,
Operation.STATUS: strategy_objects.StatusObject}
And then you can do this dictionary lookup and call the function , Example -
d[operation]()
Here is a bastardized switch/case done using dictionaries:
For example:
# define the function blocks
def start():
strategy = strategy_objects.StartObject()
def stop():
strategy = strategy_objects.StopObject()
def status():
strategy = strategy_objects.StatusObject()
# map the inputs to the function blocks
options = {"start" : start,
"stop" : stop,
"status" : status,
}
Then the equivalent switch block is invoked:
options["string"]()

empty function object in python

I've heard that python functions are objects, similar to lists or dictionaries, etc. However, what would be a similar way of performing this type of action with a function?
# Assigning empty list to 'a'
a = list()
# Assigning empty function to 'a'
a = lambda: pass
# ???
How would you do this? Further, is it necessary or proper?
Here is the sense in which I would like to use it for better context:
I have a QListWidget for selecting items which are associated with keys in a dictionary. The values in this dictionary are also dictionaries, which hold certain properties of the items, which I can add. These certain properties are stored as keys, and the values in them are initialized or updated by calling different functions. So, I'm storing a variable in the window which gets updated when a button is pressed to tell this script which property to update.
As you can see, I would like to store the function to map to the data using the correct function based on the situation.
# Get selection from the list
name = selected_item
# Initialize an empty function
f = lambda: pass
# Use property that is being added now, which was updated by the specific button that was pushed
property_list = items[name][self.property_currently_being_added]
if self.property_currently_being_added == "prop1":
f = make_property1()
elif self.property_currently_being_added == "prop2":
f = make_property2()
elif self.property_currently_being_added == "prop3":
f = make_property3()
elif self.property_currently_being_added == "prop4":
f = make_property4()
# map the certain function to the data which was retrieved earlier
added_property = map(f, data)
property_list.append(added_property)
First, the reason this doesn't work:
a = lamdba: pass
… is that lambda only allows an expression, and defines a function that returns the value of the expression. Since pass is a statement, not an expression, this is illegal.
However, this works just fine:
a = lambda: None
In Python, a function that falls off the end without a return statement always returns None. So, these are equivalent:
def a(): return None
def a(): pass
However, I don't see why you want to write this as a lambda and an assignment anyway; the def is shorter, and more readable, and gives you an introspectable function object with a nice name (a instead of <lambda>), and so on. The only reasons to ever use lambda are when you don't want to give the function a name, or when you need to define the function inside an expression. Obviously neither of those are true, because you use the lambda directly inside an assignment statement. So, just use def.
Meanwhile, this is in a sense an "empty function", or at least as empty as possible (as you can see by, e.g., calling dis.dis(a), it still takes two bytecodes to do nothing but fall off the end and return None), but it's not useful for your case. You don't want an "empty function". If you try passing your a to map, you're just going to get a TypeError, because you're trying to call a function of no arguments with one argument. (Because that's what map does.)
What you might want is an identity function, which just returns its argument as-is. Like this:
def a(x): return x
But I'm not sure that's what you want. Did you want to append data as-is in that case? Or did you want to do something different, like return early, or raise an exception, or not append anything, or …?
Finally, I don't see why you want a function at all. Why not just not call map if you have nothing to map? You have a perfectly good else clause that already catches that case (especially handy if what you want to do is return early or raise…). Or, if you prefer, you can start with f = None, and then use an if f: do decide whether to map or not. Or, if you really want:
added_property = [f(element) if f else element for element in data]
… or …
added_property = map(f, data) if f else data
As one last note, instead of a long if/elif chain that repeats the same thing over and over again, you might want a dict:
propfuncs = {'prop1': make_property1(),
'prop2': make_property2(),
'prop3': make_property3(),
'prop4': make_property4()}
Then, all that cruft turns into these two lines:
f = propfuncs.get(self.property_currently_being_added)
added_property = map(f, data) if f else data
Or course an even better design might be to replace all those make_propertyN functions with a single function that you call as make_property(1) or make_property('prop1')… but without seeing what they actually do, I can't be sure of that.
For completeness and since the title is "empty function object in python", more general case is an empty function object that takes any number of parameters, so you can use it in any callback. It's this one:
callback = lambda *_, **__: None
Explanation is here: http://echochamber.me/viewtopic.php?t=64825
I am surprised to learn that you can even do...
def a(): "This is a test"
a()
this feels so much like you're looking for a Nothing functor, I am guessing that if you had knowledge of Monads you wouldn't even need an empty function , as inspiration PyMonad has a nice Nothing implementation, I usually like to create my own, but it's a good starting point.

Categories

Resources