Python's equivalent of Ruby's ||= - python

To check if a variable exist, and if exits, use the original value, other wise, use the new value assigned.
In ruby, it's
var ||= var_new
How to write it in python?
PS:
I don't know the name of ||=, I simply can't search it in Bing.

I think there is some confusion from the people who aren't really sure what the conditional assignment operator (||=) does, and also some misunderstanding about how variables are spawned in Ruby.
Everyone should read this article on the subject. A TLDR quote:
A common misconception is that a ||= b is equivalent to a = a || b, but it behaves like a || a = b
In a = a || b, a is set to something by the statement on every run, whereas with a || a = b, a is only set if a is logically false (i.e. if it's nil or false) because || is 'short circuiting'. That is, if the left hand side of the || comparison is true, there's no need to check the right hand side.
And another very important note:
...a variable assignment, even if not run, immediately summons that variable into being.
# Ruby
x = 10 if 2 == 5
puts x
Even though the first line won't be run, x will exist on the second line and no exception will be raised.
This means that Ruby will absolutely ensure that there is a variable container for a value to be placed into before any righthand conditionals take place. ||= doesn't assign if a is not defined, it assigns if a is falsy (again, false or nil - nil being the default nothingness value in Ruby), whilst guaranteeing a is defined.
What does this mean for Python?
Well, if a is defined, the following:
# Ruby
a ||= 10
is actually equivalent to:
# Python
if not a:
a = 10
while the following:
# Either language
a = a or 10
is close, but it always assigns a value, whereas the previous examples do not.
And if a is not defined the whole operation is closer to:
# Python
a = None
if not a:
a = 10
Because a very explicit example of what a ||= 10 does when a is not defined would be:
# Ruby
if not defined? a
a = nil
end
if not a
a = 10
end
At the end of the day, the ||= operator is not completely translatable to Python in any kind of 'Pythonic' way, because of how it relies on the underlying variable spawning in Ruby.

There is no particularly elegant way in python, because it's not particularly elegant to get yourself into a situation where you don't know whether a variable is existing or not. However, this seems closest:
try:
var
except NameError:
var = var_new
I'm not familiar with the ||= operator in ruby, but from what you have described this block should have the correct behaviour. That is, we leave var bound as is if it was an already existing variable, and we set it to var_new otherwise.

This is approximately, and idiomatically, what you want:
var = var or var_new
Python's rules for variables that "don't exist" are quite strict; this will throw an exception if var has not been previously assigned. However, if var evaluates as falsey, it will receive the value of var_new.
I say this is "idiomatically what you want" because the idiomatic larger structure for this kind of thing, in Python, goes like so:
var1 = None
var2 = None
var3 = None
# ... code here that may or may not set var1 through var3 ...
var1 = var1 or "default1"
var2 = var2 or "default2"
var3 = var3 or "default3"
Note also that Python has a fairly broad notion of "falsey". This construct is only valid if var cannot have been assigned zero, False, or any object considered to be "empty" (e.g. "", [], {}...). If you really want it to trigger only on None you have to write the more verbose
var = var if var is not None else var_new
and, rather than do that, I would generally look for another way to solve the larger problem.
Finally, if you can structure your code like this instead...
var1 = "default1"
var2 = "default2"
var3 = "default3"
# ... code here that may or may not set var1 through var3 ...
... then you should, because it's shorter and less complicated that way, and the None vs falsey issue is completely avoided.

Not very safe however another option would be:
var = locals().get('var', var_new)

try: var
except NameError: var = var_new
or
var = locals().get('var', var_new)

Related

Python Linking Variable Values

I want to to store a reference to a class object inside a dictionary or another class that will maintain the same value as the actual class object. Currently, I'm using a string of the class object and the eval() function. I know this isn't a proper solution but haven't found another fix.
curWeapon is a object of WeaponInfo with pveFlag and pvpFlag variables
ListSetting stores the boolean expression of curWeapon along with a,b,c...
wishLists is passed to the main class of the program which reads a file and changed the pve/pvpFlags
When a empty line is read the boolean expression is evaluated with the new flags
wishLists.append(ListSetting("curWeapon.pveFlag or not curWeapon.pvpFlag", a, b, c, d))
My only idea is making a new function that has separates the boolean expression from a,b,c.. when making the ListSetting and adding that separately. Although I'm not sure if wishLists would update the firstFlag, secondFlag... variables.
firstFlag = ListSetting(a,b,c,d)
wishLists.append(firstFlag)
def wishListFlags():
firstFlag.flags = curWeapon.pveFlag or not curWeapon.pvpFlag
secondFlag.flags = ""
...
I'm pretty sure that updating the index of wishLists would work but would need a bunch of if statements or a dictionary.
firstFlag = ListSetting(a,b,c,d)
wishLists.append(firstFlag)
flagExpressions = {
1 : curWishListcurWeapon.pveFlag or not curWeapon.pvpFlag,
2 : "",
...}
def wishListFlags():
for index in len(wishLists):
wishLists[index].flags = flagExpressions.get(index)
If anyone knows a better way to go about this please let me know. Also, if my examples aren't specific enough or are confusing I'd be happy to share my entire program, I didn't know if it would be too much.
To store an expression you use a function, which you later call to get the value of the expression.
flagExpressions = {
1: lambda: curWishListcurWeapon.pveFlag or not curWeapon.pvpFlag
2: lambda: ""
}
def wishListFlags():
for index in len(wishLists):
wishLists[index].flags = flagExpressions.get(index, lambda: None)()

Check if variable exists in Python [duplicate]

I want to check if a variable exists. Now I'm doing something like this:
try:
myVar
except NameError:
# Do something.
Are there other ways without exceptions?
To check the existence of a local variable:
if 'myVar' in locals():
# myVar exists.
To check the existence of a global variable:
if 'myVar' in globals():
# myVar exists.
To check if an object has an attribute:
if hasattr(obj, 'attr_name'):
# obj.attr_name exists.
The use of variables that have yet to been defined or set (implicitly or explicitly) is often a bad thing in any language, since it tends to indicate that the logic of the program hasn't been thought through properly, and is likely to result in unpredictable behaviour.
If you need to do it in Python, the following trick, which is similar to yours, will ensure that a variable has some value before use:
try:
myVar
except NameError:
myVar = None # or some other default value.
# Now you're free to use myVar without Python complaining.
However, I'm still not convinced that's a good idea - in my opinion, you should try to refactor your code so that this situation does not occur.
By way of an example, the following code was given below in a comment, to allow line drawing from a previous point to the current point:
if last:
draw(last, current);
last = current
In the case where last has not been bound to a value, that won't help in Python at all since even the checking of last will raise an exception. A better idea would be to ensure last does have a value, one that can be used to decide whether or not it is valid. That would be something like:
last = None
# some time passes ...
if last is not None:
draw(last, current);
last = current
That ensures the variable exists and that you only use it if it's valid for what you need it for. This is what I assume the if last was meant to do in the comment code (but didn't), and you can still add the code to force this if you have no control over the initial setting of the variable, using the exception method above:
# Variable 'last' may or may not be bound to a value at this point.
try:
last
except NameError:
last = None
# It will always now be bound to a value at this point.
if last is not None:
draw(last, current);
last = current
A simple way is to initialize it at first saying myVar = None
Then later on:
if myVar is not None:
# Do something
Using try/except is the best way to test for a variable's existence. But there's almost certainly a better way of doing whatever it is you're doing than setting/testing global variables.
For example, if you want to initialize a module-level variable the first time you call some function, you're better off with code something like this:
my_variable = None
def InitMyVariable():
global my_variable
if my_variable is None:
my_variable = ...
for objects/modules, you can also
'var' in dir(obj)
For example,
>>> class Something(object):
... pass
...
>>> c = Something()
>>> c.a = 1
>>> 'a' in dir(c)
True
>>> 'b' in dir(c)
False
I will assume that the test is going to be used in a function, similar to user97370's answer. I don't like that answer because it pollutes the global namespace. One way to fix it is to use a class instead:
class InitMyVariable(object):
my_variable = None
def __call__(self):
if self.my_variable is None:
self.my_variable = ...
I don't like this, because it complicates the code and opens up questions such as, should this confirm to the Singleton programming pattern? Fortunately, Python has allowed functions to have attributes for a while, which gives us this simple solution:
def InitMyVariable():
if InitMyVariable.my_variable is None:
InitMyVariable.my_variable = ...
InitMyVariable.my_variable = None
catch is called except in Python. other than that it's fine for such simple cases. There's the AttributeError that can be used to check if an object has an attribute.
A way that often works well for handling this kind of situation is to not explicitly check if the variable exists but just go ahead and wrap the first usage of the possibly non-existing variable in a try/except NameError:
# Search for entry.
for x in y:
if x == 3:
found = x
# Work with found entry.
try:
print('Found: {0}'.format(found))
except NameError:
print('Not found')
else:
# Handle rest of Found case here
...
I created a custom function.
def exists(var):
return var in globals()
Then the call the function like follows replacing variable_name with the variable you want to check:
exists("variable_name")
Will return True or False
Like so:
def no(var):
"give var as a string (quote it like 'var')"
assert(var not in vars())
assert(var not in globals())
assert(var not in vars(__builtins__))
import keyword
assert(var not in keyword.kwlist)
Then later:
no('foo')
foo = ....
If your new variable foo is not safe to use, you'll get an AssertionError exception which will point to the line that failed, and then you will know better.
Here is the obvious contrived self-reference:
no('no')
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-88-d14ecc6b025a> in <module>
----> 1 no('no')
<ipython-input-86-888a9df72be0> in no(var)
2 "give var as a string (quote it)"
3 assert( var not in vars())
----> 4 assert( var not in globals())
5 assert( var not in vars(__builtins__))
6 import keyword
AssertionError:
It may not be performant, but you generalise the solution to a function that checks both local variables and global variables.
import inspect
def exists_var(var_name):
frame = inspect.currentframe()
try:
return var_name in frame.f_back.f_locals or var_name in globals()
finally:
del frame
Then you can use it like this:
exists_var('myVar')
Short variant:
my_var = some_value if 'my_var' not in globals() else my_var:
This was my scenario:
for i in generate_numbers():
do_something(i)
# Use the last i.
I can’t easily determine the length of the iterable, and that means that i may or may not exist depending on whether the iterable produces an empty sequence.
If I want to use the last i of the iterable (an i that doesn’t exist for an empty sequence) I can do one of two things:
i = None # Declare the variable.
for i in generate_numbers():
do_something(i)
use_last(i)
or
for i in generate_numbers():
do_something(i)
try:
use_last(i)
except UnboundLocalError:
pass # i didn’t exist because sequence was empty.
The first solution may be problematic because I can’t tell (depending on the sequence values) whether i was the last element. The second solution is more accurate in that respect.
Also a possibility for objects, use __dict__.
class A(object):
def __init__(self):
self.m = 1
a = A()
assert "m" in a.__dict__
assert "k" not in a.__dict__

Python "if" statement - if xpath is true

im trying to code in python (very new to it) and need to check if an xpath is there then variable = the xpath but if not variable = string.
An example is below
if tree.xpath('//*#id="page"]/div[1]/div/main/div/article/div/div[1]/section[2]/p[1]/span/text()') = true
$value = tree.xpath('//*#id="page"]/div[1]/div/main/div/article/div/div[1]/section[2]/p[1]/span/text()')
else
$value = ''
You should really start by doing the whole official tutorial before anything else, as it will anwser your question.
First point : Python objects all have a 'truth' value in a boolean context, which is defined by the object's type and actual value. For builtin types, all empty containers (lists, dicts, sets etc), the empty string, all numerical zeros and the None object are false, everything else is true. For non builtin types you'll have to check the package's documentation.
The builtin type bool will also tell you the boolean value of a given object, so all of the below tests are equivalent:
if myobj:
xxx
if bool(myobj):
xxx
if bool(myobj) == True:
xxx
BUT keep in mind that it doesn't imply that bool(myobj) is the same as myobj - the first one is the boolean value of your object, so the following is NOT equivalent (unless myobj is one of True, 1 or 1.0):
if myobj == True:
xxx
Now wrt/ your actual code snippet: it's not valid Python (bad indentation, invalid identifier $value, invalid use of the assignment operator =, missing : after the if and else statements, wrong capitalization for True...)
Assuming you meant:
# let's make this at least readable:
path = '//*#id="page"]/div[1]/div/main/div/article/div/div[1]/section[2]/p[1]/span/text()'
if tree.xpath(path) == True:
value = tree.xpath(path)
else:
value = ''
The obvious error is the explicit test against True (tree.xpath() doesn't return a boolean). You either have to explicitely cast the return of tree.xpath() to a boolean (which is quite verbose, totally useless and definitly unpythonic) or just let Python do the right thing by removing the == True part of your test.
As a side note: calling tree.xpath twice in a row with the same argument is a waste of processor cycle (both calls will return the same value), so use a variable instead - it will also make your code much more readable and maintainable. The Pythonic version of your code would look something like:
path = '//*#id="page"]/div[1]/div/main/div/article/div/div[1]/section[2]/p[1]/span/text()'
found = tree.xpath(path)
value = found if found else ''
or even more simply:
path = '//*#id="page"]/div[1]/div/main/div/article/div/div[1]/section[2]/p[1]/span/text()'
value = tree.xpath(path) or ''
since the or operator will not yield a boolean value but either the first of it's operand that has a true value or the last operand if none has a true value.
#No need to test == if value is bool. and you not check, you assign value with one =
if anything:
#Then do this
else:
#Do this

Switch in Python [duplicate]

This question already has answers here:
Replacements for switch statement in Python?
(44 answers)
Closed 27 days ago.
I have tried making a switch like statement in python, instead of having a lot of if statements.
The code looks like this:
def findStuff(cds):
L=[]
c=0
for i in range(0, len(cds), 3):
a=differencesTo(cds[i:i+3])
result = {
a[2][0]==1: c=i+1,
a[2][1]==1: c=i+2,
a[2][2]==1: c=i+3,
a[1]==1: L.append((cds[i:i+3], a[0], c))
}
return L
My problem is, that this does not work. (Works with if statements, but this would in my opinion be more pretty).
I have found some examples of switches in Python, and they follow this structure. Can anyone help me?
(a) I fail to see what is wrong with if...elif...else
(b) I assume that python does not have a switch statement for the same reason that Smalltalk doesn't: it's almost completely redundant, and in the case where you want to switch on types, you can add an appropriate method to your classes; and likewise switching on values should be largely redundant.
Note: I am informed in the comments that whatever Guido's reason for not creating a switch in the first place, PEPs to have it added were rejected on the basis that support for adding such a statement is extremely limited. See: http://www.python.org/dev/peps/pep-3103/
(c) If you really need switching behaviour, use a hashtable (dict) to store callables. The structure is:
switch_dict = {
Foo: self.doFoo,
Bar: self.doBar,
}
func = switch_dict[switch_var]
result = func() # or if they take args, pass args
There's nothing wrong with a long if:
if switch == 'case0':
do_case0()
elif switch == 'case1':
do_case1()
elif switch == 'case2':
do_case2()
...
If that's too long winded, or if you have a lot of cases, put them in a dictionary:
switch = {'case0': do_case0, 'case1': do_case1, 'case2': do_case2, ...}
switch[case_variable]()
// Alternative:
(switch[case_variable]).__call__()
If your conditions are a bit more complex, you need to think a little about your data structures. e.g.:
switch = {
(0,21): 'never have a pension',
(21,50): 'might have a pension',
(50,65): 'definitely have a pension',
(65, 200): 'already collecting pension'
}
for key, value in switch.items():
if key[0] <= case_var < key[1]:
print(value)
Other ans are suitable for older version of python. For python v3.10+ you can use match/case which is more powerful than general switch/case construct.
def something(val):
match val:
case "A":
return "A"
case "B":
return "B"
case "C":
return "C"
case _:
return "Default"
something("A")
Assignment in Python is a statement, and cannot be a part of expression. Also, using literal in this way evaluates everything at once, which is probably not what you want. Just use ifs, you won't gain any readability by using this.
I don't know which article you've found to do something like this, but this is really messy: the whole result diction will be always evaluated, and instead of doing only part of the work (as a switch / if do), you'll do the whole work everytime. (even if you use only a part of the result).
Really, a fast switch statement in Python is using "if":
if case == 1:
pass
elif case == 2:
pass
elif case == 3:
pass
else:
# default case
pass
With "get" method, you can have the same effect as "switch..case" in C.
Marcin example :
switch_dict = {
Foo: self.doFoo,
Bar: self.doBar,
}
func = switch_dict.get(switch_var, self.dodefault)
result = func() # or if they take args, pass args
You can do something like what you want, but you shouldn't. That said, here's how; you can see how it does not improve things.
The biggest problem with the way you have it is that Python will evaluate your tests and results once, at the time you declare the dictionary. What you'd have to do instead is make all conditions and the resulting statements functions; this way, evaluation is deferred until you call them. Fortunately there is a way to do this inline for simple functions using the lambda keyword. Secondly, the assignment statement can't be used as a value in Python, so our action functions (which are executed if the corresponding condition function returns a truthy value) have to return a value that will be used to increment c; they can't assign to c themselves.
Also, the items in a dictionary aren't ordered, so your tests won't necessarily be performed in the order you define them, meaning you probably should use something other than a dictionary that preserves order, such as a tuple or a list. I am assuming you want only ever one case to execute.
So, here we go:
def findStuff(cds):
cases = [ (lambda: a[2][0] == 1, lambda: i + 1),
(lambda: a[2][1] == 1, lambda: i + 2),
(lambda: a[2][2] == 1, lambda: i + 3),
(lambda: a[1] == 1, lambda: L.append(cds[i:i+3], a[0], c) or 0)
]
L=[]
c=0
for i in range(0, len(cds), 3):
a=differencesTo(cds[i:i+3])
for condition, action in cases:
if condition():
c += action()
break
return L
Is this more readable than a sequence of if/elif statements? Nooooooooooooo. In particular, the fourth case is far less comprehensible than it should be because we are having to rely on a function that returns the increment for c to modify a completely different variable, and then we have to figure out how to get it to return a 0 so that c won't actually be modified. Uuuuuugly.
Don't do this. In fact this code probably won't even run as-is, as I deemed it too ugly to test.
While there is nothing wrong with if..else, I find "switch in Python" still an intriguing problem statement. On that, I think Marcin's (deprecated) option (c) and/or Snim2's second variant can be written in a more readable way.
For this we can declare a switch class, and exploit the __init__() to declare the case we want to switch, while __call__() helps to hand over a dict listing the (case, function) pairs:
class switch(object):
def __init__(self, case):
self._case = case
def __call__(self, dict_):
try:
return dict_[self._case]()
except KeyError:
if 'else' in dict_:
return dict_['else']()
raise Exception('Given case wasn\'t found.')
Or, respectively, since a class with only two methods, of which one is __init__(), isn't really a class:
def switch(case):
def cases(dict_):
try:
return dict_[case]()
except KeyError:
if 'else' in dict_:
return dict_['else']()
raise Exception('Given case wasn\'t found.')
return cases
(note: choose something smarter than Exception)
With for example
def case_a():
print('hello world')
def case_b():
print('sth other than hello')
def default():
print('last resort')
you can call
switch('c') ({
'a': case_a,
'b': case_b,
'else': default
})
which, for this particular example would print
last resort
This doesn't behave like a C switch in that there is no break for the different cases, because each case executes only the function declared for the particular case (i.e. break is implicitly always called). Secondly, each case can list exactly only one function that will be executed upon a found case.

Python shorthand for conditionally assigning a variable

I find myself writing stuff like this too often and it seems too wordy:
obj = my_dict.get('obj')
if obj:
var = obj
Is there a better way to do this? Maybe in one line?
The get function takes a second argument, a default:
get(key[, default])
Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.
http://docs.python.org/library/stdtypes.html
So, you could use the below to replicate your question's code:
var = my_dict.get('obj', var)
There's no clean way to replace this code in one line, because it conditionally binds the name var. In the case that var was already defined, this one-liner is possible:
var = my_dict['obj'] if 'obj' in my_dict and my_dict['obj'] else var
However, this is still slightly different than the original code in case var was not already defined: the one-liner is raising a NameError and the original code just continues with var unbound.
Note that other answers behave differently when the value exists but is falsey.
obj = my_dict.get('obj')
if obj: # <-- test on truthiness of obj
# if we are here, it means:
# 1. my_dict has key 'obj', AND...
# 2. at least one of the following,
# my_dict['obj'].__bool__() returned True (__nonzero__ for Python 2)
# OR
# my_dict['obj'].__len__() returned result > 0
# OR
# my_dict['obj'] is not None, and has neither __bool__ nor __len__ defined
var = obj
I find this form to be the most explicitly readable and I use it all the time:
moo = animals["cow"] if "cow" in animals else None
Assuming that my_dict is what it says it is (a dictionary):
var = my_dict.get('obj') if 'obj' in my_dict else var
Edited to preserve intent with original question.
var = thedict.get('thekey', var)

Categories

Resources