Proper way to do a True/False/"something" argument in Python [closed] - python

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 18 days ago.
Improve this question
I have a function in Python which should use a keyword argument, let's call it foo, to decide if a certain action should be performed always (foo=True), never (foo=False) or let an algorithm make the decision (foo='auto'). Minimum working example would look something like this:
def frobble(bar, foo=True):
if foo == "auto":
print("do something automatically")
elif foo:
print("always do the thing")
else:
print("never do the thing")
return bar
With calls to the function:
frobble(my_bar, True)
frobble(my_bar, False)
frobble(my_bar, "auto")
And while it works like this (thanks to Python being dynamically typed I guess), I just don't think it's the best solution to let foo be a bool sometimes and a str at other times. Especially considering that a non-empty string is considered True and an empty string is considered False in Python. This might lead to some issues when comparing it, a.k.a. an empty string might be passed, without desiring the behavior associated with passing False. Now, I could of course always let the argument be of str type and do foo='True' instead of foo=True and so on. But that also strikes me as potentially a little confusing to the user.
Is there an obvious (and "pythonic") way out of this that I'm missing?

At least on my end, PyCharm complains on the last line with a type warning:
Expected type 'bool', got 'str' instead.
I, for one, feel that what you're doing isn't too far-fetched. It might not be the most Pythonic or obvious to have a function parameter sometimes be a bool and other times a specific str value, however for clarity sake this can potentially be simplified.
This simplification step derives from the fact that you are only checking for a specific str value ("auto").
For a specific example, you can use a sentinel object to detect if another type (other than bool) is passed in -- similar to how dataclasses does it:
from __future__ import annotations
from typing import TYPE_CHECKING
# minor optimization: this `class` def block is only useful for IDEs
if TYPE_CHECKING:
class MissingType:
pass
MISSING = MissingType()
else:
from dataclasses import MISSING
def frobble(bar, foo: bool | MissingType = True):
if foo is MISSING:
print("do something automatically")
elif foo:
print("always do the thing")
else:
print("never do the thing")
return bar
my_bar = 0
frobble(my_bar, True)
frobble(my_bar, False)
frobble(my_bar, MISSING)
If the thought is that it's still more intuitive for a user to pass a string value of "auto" into the function, you can also constrain the value of str that are passed into the function, so that the IDE can help warn about any potential typing issues when writing the code.
In this case, I would suggest typing.Literal:
from __future__ import annotations
from typing import Literal
def frobble(bar, foo: bool | Literal['auto'] = True):
if foo == 'auto':
print("do something automatically")
elif foo:
print("always do the thing")
else:
print("never do the thing")
return bar
my_bar = 0
frobble(my_bar, True)
frobble(my_bar, False)
frobble(my_bar, "auto")

I don't see a problem with your current frobble() but I'll give you an alternate pattern that I find is sometimes helpful.
It is based on using a dictionary of parameter values and methods to determine what work is to be done in the event that the work is not trivial. For trivial work, I would certainly use your pattern.
def frobble(bar, foo="auto"):
def on_true(bar):
print("always do the thing")
return bar
def on_false(bar):
print("never do the thing")
return bar
def on_auto(bar):
print("do something automatically")
return bar
work = {
True: on_true,
False: on_false,
"auto": on_auto
}
return work.get(foo, on_auto)(bar)
frobble(1, True)
frobble(1, False)
frobble(1, "auto")
frobble(1)
frobble(1, "?")
Note that is treats any "other" value as if it was "auto" and that might not be what you want. You might alternatively do:
def frobble(bar, foo="auto"):
def on_true(bar):
print("always do the thing")
return bar
def on_false(bar):
print("never do the thing")
return bar
def on_auto(bar):
print("do something automatically")
return bar
work = {
True: on_true,
False: on_false,
"auto": on_auto
}
return work[foo](bar)
frobble(1, True)
frobble(1, False)
frobble(1, "auto")
frobble(1)
frobble(1, "?")
That will throw a KeyError on frobble(1, "?") but you could catch that and throw a ValueError instead if you wanted.
If frobble() was a thing that you were going to do "a lot" of, I might build a closure to support it:
def make_frobble():
def on_true(bar):
print("always do the thing")
return bar
def on_false(bar):
print("never do the thing")
return bar
def on_auto(bar):
print("do something automatically")
return bar
work = {
True: on_true,
False: on_false,
"auto": on_auto
}
return lambda bar, foo="auto": work[foo](bar)
frobble = make_frobble()
frobble(1, True)
frobble(1, False)
frobble(1, "auto")
frobble(1)
frobble(1, "?")

Related

Switch Implementation of Object calling function in python

I am trying to implement the switch in Dictionary in Python. But when I call the method
chooices.get(x, "Ther is no program") It call all the Function in the statment rather than calling x function.
I have read this implementation
Replacements for switch statement in Python?
but it was not helpful in my case as my function has print statement
Main file contain the code
from Day1.Day1 import Day1
from Day2.TipCalculator import TipCalculator
def choose():
print("Choose From the following Program:\n")
day1 = Day1()
day1.day1()
day2 = TipCalculator()
day2.day2()
x = int(input());
chooices={
1: day1.program1(),
2: day2.program1(),
}
chooices.get(x, "Ther is no program")
choose()
Day1 Class contain code
class Day1:
def day1(self):
print('1. Band name Generator\n')
def program1(self):
print('Welcome to Band Name Generator.\n')
cityName=input('What\'s is the name of city you grew up?\n')
petName = input('What\'s your pet\'s name?\n')
print('Your Band Name could be : '+cityName+" "+petName)
Class Tip Calculator Code
class TipCalculator:
def day2(self):
print("2. Tip Calculator For Bill.\n")
def program1(self):
print('Welcome to tip calculator.\n')
I just need the implementation Of switch statement which call Requested Program just like switch. I know Its possible through If-else but Dictionary mapping seems too be good alternative of switch
Overview: the interpretor creates a closure for the variables that the child function uses that are non local. In this example the child variable, value is 22 and stored in the closure cell.
def parent(arg_1, arg_2):
value=22
my_dict = {'chocolate':'yummy'}
def child():
print(2*value)
print(my['chocolate'])
print(arg_1 + arg_2)
return child
new_function=parent(3,4)
print(cell.cell_contents for cell in new_function.__closure__])
If you don't have a lot of variants, the if/elif/else statements can be streamlined using a helper function for the switch. This is only syntactic candy but it may be sufficient for small value sets.
def switch(v): yield lambda *c: v in c
Example usage:
x = int(input())
for case in switch(x):
if case(1): day1.program1()
elif case(2): day2.program1()
else: print("there is no program")
supporting multiple values for the same method call:
x = int(input())
for case in switch(x):
if case(1,5): day1.program1()
elif case(2,3,4): day2.program1()
else: print("there is no program")
you can also use it in a more C-like style
x = int(input())
for case in switch(x):
if case(1,5):
day1.program1()
break
if case(2,3,4):
day2.program1()
break
else:
print("there is no program")
If you have python 3.10 or higher, a proper switch analogue was implmemented called "match" which should work quite well in replacing any nested if statments the other answers may have.
If you dont have 3.10, and you are okay with a pretty hacky solution, mine uses the ideas from withhacks (specifically from AnonymousBlocksInPython). I have recently created my own version of a switch statment in python that acts more like how i am used to in C#. You can expand this as much as you want, way past single line arguments or assignments.
It uses context managers so that you can treat each case as its own code block with indentation and all. It will never enter the cases if the case value does not match the switch value so for code that is extremely system taxing, you can be sure it is not running code that does not need to be.
import sys
class CaseReturn(Exception):pass
class Case:
def __init__(self, caseVal): self._caseVal_ = caseVal
def _trace_(self,frame,event,arg): raise CaseReturn
def __enter__(self):
if self._caseVal_ == Switch._singleton_._switch_: return
sys.settrace(lambda *args, **keys: None)
sys._getframe(1).f_trace= self._trace_
def __exit__(self,ExType,ExVal,ExTrace):
if ExType is None: raise CaseReturn
return ExType is CaseReturn
class Switch:
_singleton_:'Switch' = None
def __init__(self, switchVal,Default=None): self._switch_ = switchVal
def __enter__(self):Switch._singleton_ = self
def __exit__(self,ExType,ExVal,ExTrace):
Switch._singleton_ = None
return ExType is CaseReturn
with Switch(2):
with Case(1):
print('This should not make it')
with Case(2):
print('I made it')
with Case(3):
print('This should never be called')
You can easily extend this out to check multiple cases by just changing the caseVal to a list and doing if Switch._singleton_._switch_ in self._caseVal_:
One caveat is, you cannot make the Case statments one-liners like:
Case(0): print('I am one line')
That will not work and end up calling the code in that case no matter what.
I hope this is useful to you or anyone who is searching for custom Switch statments!

How to best remove redundant calls from a code block, that should be executed when the block exists successfully?

I'm trying to simplify the following code (removing the redundant prints), but can't find a satisfying way to do this:
original code
def main():
if expression1:
print("1")
print("always_do_this")
return
if expression2:
print("2")
print("always_do_this")
return
# ... possibly more expressions and thus redundancy
print("always_do_this")
# do something else
My first idea was a try-(except-)else combination, but the else is not executed on a return in the try-block.
Solution 1 - extracting into a separate function
def func():
if expression1:
print("1")
return True
if expression2:
print("2")
return True
return False
def main():
result = func()
print("always_do_this")
if result:
return
# do something else
Solution 2 - workaround using finally
def main():
error = False
try:
if expression1:
print("1")
return
if expression2:
print("2")
return
except:
error = True
raise
finally:
if not error:
print("always_do_this")
# do something else
Surely there must be a better way to achieve this in python?
PS: Also any ideas for a better title would be appreciated...
PPS: I'm not directly asking about (subjective) codestyle, but wondering if there is a way to write this that I didn't consider (e.g. a language construct/pattern, that also makes the code more concise/cleaner; obviously there are a lot worse ways).
Check if your flow did not enter the first two if blocks by checking for the opposite of the first two if statements joined by an andso that you can execute "do something else" only if the first two if statements failed. Return at the end instead of in the middle of the if statements.
def main():
expression1 = True
expression2 = False
if expression1:
print("1")
elif expression2:
print("2")
print("always_do_this")
if not expression1 and not expression2:
# do something else
return
If the thing you always want to do is closing a file, I would use a with statement. In a more general case you can create your own context manager to have full control of what gets run at the end.
Sample code:
class my_closer:
def __enter__(self):
return True
def __exit__(self, type, value, traceback):
if type is None:
print("always_do_this")
else
print("An exception was raised: {}".format(type))
def main():
with my_closer() as c:
if someexpr:
print("1")
return
if someexpr:
print("2")
return
I added a superfluous else to print something about the exception in case of error, but leave it out to reproduce your original code more accurately.
This code is not shorter than yours with the trivial print statement, but I like this method for more complex "closer" code.
You can also define the context manager this way using the contextlib library:
from contextlib import contextmanager
#contextmanager
def my_closer(*args, **kwds):
try:
yield True
except:
#print("some error happened")
raise
else:
print("always_do_this")
References:
http://effbot.org/zone/python-with-statement.htm
https://docs.python.org/3/library/stdtypes.html#typecontextmanager
https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager

Code returns "none" on codecademy and im not sure why

Instructions:
First, def a function called distance_from_zero, with one argument (choose any >argument name you like).
If the type of the argument is either int or float, the function should return >the absolute value of the function input.
Otherwise, the function should return "Nope"
I've done the first task and I thought that i completed the task, however
"Your function seems to fail on input True when it returned 'None' instead of 'Nope'"
Here is my code:
def distance_from_zero(argument):
if type(argument) == int or type(argument) == float:
return abs(argument)
print(argument)
else:
print("Nope")
From what ive seen in other modules codecademy tests this with my arguement being "1", such that the if statement goes through, will return the absolute value of (argument) and then i would pass the module. (i added print(argument) for testing purposes, the console outputs nothing.)
Am i mis understanding how returning works? Why is this not working?
I appreciate all responses! :)
EDIT: It prints "None", not "Nope" in the console. Forgot to mention this.
In Python, if return isn't explicit, it becomes None. Try this:
def distance_from_zero(argument):
if type(argument) == int or type(argument) == float:
print(abs(argument))
else:
print("Nope")
> f = distance_from_zero(-4234)
4234
> f
None
As you can see the value of f is None, this is because print is outputting to the console and not actively returning content. Instead, try using the return statement:
def distance_from_zero(argument):
if type(argument) == int or type(argument) == float:
return abs(argument)
else:
return "Nope"
> distance_from_zero(123)
123
# here, because you're not assigning the value returned to a variable, it's just output.
> f = distance_from_zero(-4234)
> f
4234
> f = distance_from_zero('cat')
> f
'Nope'
It's also important to know that the reason this:
return abs(argument)
print(argument)
printed to the console is not because of the print call. Anything after return in a block is not executed. The reason you see the output printed to the screen is because in the interpreter Python outputs all function return values not collected into variables.
def distance_from_zero(num):
if type(num) == int or type(num) == float:
return abs(num)
else:
return "Nope"

How do I use dictionaries to make functions easily accessable

I declared 3 functions earlier, this is just a goofy text based cookie clicker-esque game.
dostuff={"" : turn() , "help" : helpf() , "invest" : invest() }
while done != True:<br>
do = input("What do you want to do? ")
do = do.lower()
if do == "" or do == "help" or do == "invest":
dostuff[do]
elif do == "quit":
done = True
So when I use dostuff["turn"] it does nothing (the function is supposed to print some things). I have the same problem with the other options.
Your parentheses must be omitted in the dict, and then put at the end of the dict call. You define a function, which becomes a python object. You reference the object with the dict, and then you call the function with the object reference followed by parentheses:
def one():
print("one")
def two():
print("two")
do_stuff = {
"one": one,
"two": two
}
do_stuff["one"]()
prints:
"one"
You can take this concept of executing calls with string inputs a lot farther by familiarizing yourself with the builtin functions of python.
https://docs.python.org/2/library/functions.html
For example, you can create a class and call its methods or properties using text based input with the getattr method:
class do_stuff():
def __init__(self):
pass
def one(self):
print("one")
def two(self):
print("two")
doer = do_stuff()
inp = "one"
getattr(doer, inp)()
prints->
"one"

Workaround for equality of nested functions

I have a nested function that I'm using as a callback in pyglet:
def get_stop_function(stop_key):
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
return stop_on_key
pyglet.window.set_handler('on_key_press', get_stop_function('ENTER'))
But then I run into problems later when I need to reference the nested function again:
pyglet.window.remove_handler('on_key_press', get_stop_function('ENTER'))
This doesn't work because of the way python treats functions:
my_stop_function = get_stop_function('ENTER')
my_stop_function is get_stop_function('ENTER') # False
my_stop_function == get_stop_function('ENTER') # False
Thanks to two similar questions I understand what is going on but I'm not sure what the workaround is for my case. I'm looking through the pyglet source code and it looks like pyglet uses equality to find the handler to remove.
So my final question is: how can I override the inner function's __eq__ method (or some other dunder) so that identical nested functions will be equal?
(Another workaround would be to store a reference to the function myself, but that is duplicating pyglet's job, will get messy with many callbacks, and anyways I'm curious about this question!)
Edit: actually, in the questions I linked above, it's explained that methods have value equality but not reference equality. With nested functions, you don't even get value equality, which is all I need.
Edit2: I will probably accept Bi Rico's answer, but does anyone know why the following doesn't work:
def get_stop_function(stop_key):
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
stop_on_key.__name__ = '__stop_on_' + stop_key + '__'
stop_on_key.__eq__ = lambda x: x.__name__ == '__stop_on_' + stop_key + '__'
return stop_on_key
get_stop_function('ENTER') == get_stop_function('ENTER') # False
get_stop_function('ENTER').__eq__(get_stop_function('ENTER')) # True
You could create a class for your stop functions and define your own comparison method.
class StopFunction(object):
def __init__(self, stop_key):
self.stop_key = stop_key
def __call__(self, symbol, _):
if symbol == getattr(pyglet.window.key, self.stop_key):
pyglet.app.exit()
def __eq__(self, other):
try:
return self.stop_key == other.stop_key
except AttributeError:
return False
StopFunciton('ENTER') == StopFunciton('ENTER')
# True
StopFunciton('ENTER') == StopFunciton('FOO')
# False
the solution is to keep a dictionary containing the generated functions around,
so that when you make the second call, you get the same object as in the first call.
That is, simply build some memoization logic, or use one of the libraries
existing with memoizing decorators:
ALL_FUNCTIONS = {}
def get_stop_function(stop_key):
if not stop_key in ALL_FUNCTIONS:
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
ALL_FUNCTIONS[stop_key] = stop_on_key
else:
stop_on_key = ALL_FUNCTIONS[stop_key]
return stop_on_key
You can generalize Bi Rico's solution to allow wrapping any functions up with some particular equality function pretty easily.
The first problem is defining what the equality function should check. I'm guessing for this case, you want the code to be identical (meaning functions created from the same def statement will be equal, but two functions created from character-for-character copies of the def statement will not), and the closures to be equal (meaning that if you call get_stop_function with two equal but non-identical stop_keys the functions will be equal), and nothing else to be relevant. But that's just a guess, and there are many other possibilities.
Then you just wrap a function the same way you'd wrap any other kind of object; just make sure __call__ is one of the things you delegate:
class EqualFunction(object):
def __init__(self, f):
self.f = f
def __eq__(self, other):
return (self.__code__ == other.__code__ and
all(x.cell_contents == y.cell_contents
for x, y in zip(self.__closure__, other.__closure__)))
def __getattr__(self, attr):
return getattr(self.f, attr)
def __call__(self, *args, **kwargs):
return self.f(*args, **kwargs)
If you want to support other dunder methods that aren't required to go through getattr (I don't think any of them are critical for functions, but I could be wrong…), either do it explicitly (as with __call__) or loop over them and add a generic wrapper to the type for each one.
To use the wrapper:
def make_f(i):
def f():
return i
return EqualFunction(f)
f1 = f(0)
f2 = f(0.0)
assert f1 == f2
Or, notice that EqualFunction actually works as a decorator, which may be more readable.
So, for your code:
def get_stop_function(stop_key):
#EqualFunction
def stop_on_key(symbol, _):
if symbol == getattr(pyglet.window.key, stop_key):
pyglet.app.exit()
return stop_on_key

Categories

Resources