What does colon equal (:=) in Python mean? - python

What does the := operand mean, more specifically for Python?
Can someone explain how to read this snippet of code?
node := root, cost = 0
frontier := priority queue containing node only
explored := empty set

Updated answer
In the context of the question, we are dealing with pseudocode, but starting in Python 3.8, := is actually a valid operator that allows for assignment of variables within expressions:
# Handle a matched regex
if (match := pattern.search(data)) is not None:
# Do something with match
# A loop that can't be trivially rewritten using 2-arg iter()
while chunk := file.read(8192):
process(chunk)
# Reuse a value that's expensive to compute
[y := f(x), y**2, y**3]
# Share a subexpression between a comprehension filter clause and its output
filtered_data = [y for x in data if (y := f(x)) is not None]
See PEP 572 for more details.
Original Answer
What you have found is pseudocode
Pseudocode is an informal high-level description of the operating
principle of a computer program or other algorithm.
:= is actually the assignment operator. In Python this is simply =.
To translate this pseudocode into Python you would need to know the data structures being referenced, and a bit more of the algorithm implementation.
Some notes about psuedocode:
:= is the assignment operator or = in Python
= is the equality operator or == in Python
There are certain styles, and your mileage may vary:
Pascal-style
procedure fizzbuzz
For i := 1 to 100 do
set print_number to true;
If i is divisible by 3 then
print "Fizz";
set print_number to false;
If i is divisible by 5 then
print "Buzz";
set print_number to false;
If print_number, print i;
print a newline;
end
C-style
void function fizzbuzz
For (i = 1; i <= 100; i++) {
set print_number to true;
If i is divisible by 3
print "Fizz";
set print_number to false;
If i is divisible by 5
print "Buzz";
set print_number to false;
If print_number, print i;
print a newline;
}
Note the differences in brace usage and assignment operator.

PEP572 proposed support for the := operator in Python to allow variable assignments within expressions.
This syntax is available in Python 3.8.

This symbol := is an assignment operator in Python (mostly called as the Walrus Operator). In a nutshell, the walrus operator compresses our code to make it a little shorter.
Here's a very simple example:
# without walrus
n = 30
if n > 10:
print(f"{n} is greater than 10")
# with walrus
if (n := 30) > 10:
print(f"{n} is greater than 10")
These codes are the same (and outputs the same thing), but as you can see, the version with the walrus operator is compressed in just two lines of code to make things more compact.
Now, why would you use the walrus operator?
First off, don't feel obligated.
I myself even rarely use this one. I'm just using the walrus operator to compress my code a little bit, mostly when I'm working with regular expressions.
You can also find your own use case of this. What's important is you have a rough idea about it and knows when it might be helpful when you encountered a problem like this one.
And this is by far how can I explain walrus operator in a higher level. Hope you learned something.

The code in the question is pseudo-code; there, := represents assignment.
For future visitors, though, the following might be more relevant: the next version of Python (3.8) will gain a new operator, :=, allowing assignment expressions (details, motivating examples, and discussion can be found in PEP 572, which was provisionally accepted in late June 2018).
With this new operator, you can write things like these:
if (m := re.search(pat, s)):
print m.span()
else if (m := re.search(pat2, s):
…
while len(bytes := x.read()) > 0:
… do something with `bytes`
[stripped for l in lines if len(stripped := l.strip()) > 0]
instead of these:
m = re.search(pat, s)
if m:
print m.span()
else:
m = re.search(pat2, s)
if m:
…
while True:
bytes = x.read()
if len(bytes) <= 0:
return
… do something with `bytes`
[l for l in (l.stripped() for l in lines) if len(l) > 0]

Happy 3.8 Release on 14th of October!
There is new syntax := that assigns values to variables as part of a larger expression. It is affectionately known as “the walrus operator” due to its resemblance to the eyes and tusks of a walrus.
In this example, the assignment expression helps avoid calling len() twice:
if (n := len(a)) > 10:
print(f"List is too long ({n} elements, expected <= 10)")
What’s New In Python 3.8 - Assignment expressions

:= is also called as Walrus Operator.
We can use this walrus operator to assign a value and do condition check at the same time.
Eg:
Without Walrus Operator:
a = 10
if a == 10:
print("yes")
With Walrus Operator:
if (a := 10) == 10:
print("Yes")
So, we can use variable a not just in statement also after that. it will simply assign new value into variable and enables condition check.

Related

How to stop short circuiting in Python?

Python short circuits the logical operators.
for eg:
if False and Condition2:
#condition2 won't even be checked because the first condition is already false.
Is there a way to stop this behavior. I want it to check both the conditions and then perform the and operation(as done in c, c++ etc). It's useful when we are performing some operation along with the condition. e.g.:
if a < p.pop() and b < p.pop():
One way can be checking the conditions before and then comparing the Boolean values. But that would be wastage of memory.
if all([a < p.pop(), b < p.pop()])
This creates a list, which will be evaluated in its entirety, and then uses all to confirm that both values are truthy. But this is somewhat obscure and I'd rather suggest you write plain, easy to understand code:
a_within_limit = a < p.pop()
b_within_limit = b < p.pop()
if a_within_limit and b_within_limit:
If the conditions are booleans, as they are in your example, you could use & instead:
>>> a, b, p = 1, 1, [0, 0]
>>> (a < p.pop()) & (b < p.pop())
False
>>> p
[]
You can use the all() and any() built-in functions to somehow emulate the and and or operators. Both take an iterable of boolean-likes values as parameter. If you give it a literal tuple or list, all members will be fully evaluated:
# all emulates the and operator
if all((False, Condition2)):
do_stuff()
# any emulates the or operator
if any((False, Condition2)):
do_stuff()
Short answer: No, you cannot stop it to do this.
For example:
av = p.pop()
bv = p.pop()
if a < av and b < bv:
pass
Or:
av, bv = p.pop(), p.pop()
if a < av and b < bv:
pass
Also, there is no waste of memory in these examples. In Python, almost everything is done by reference. The value object being popped already exists somewhere. Even the scalars like strings, ints, etc are objects (some of them are slightly optimized). The only memory changes here are (1) the creation of a new variable that refers to the same existing object, and (2) removal of the record in the dict at the same time (which referred to that object before popping). They are of the similar scale.

How to decrement a variable while printing in Python?

Some if and else's can be rewritten1 and shortened2 (codegolf-style) likewise, because booleans can act as integers in Python. For example if a<b:return a can be rewritten3 as return("",a)[a<b].
In this case (I simplified the condition for readability),
if a<b: print(a)
can be rewritten as both of the following:
print(("",a)[a<b])
(print(""),print(a))[a<b]
(if we ignore newlines, else end="" can be used).
I would like to decrement a variable n (the whole thing is in a while loop with n in its condition) when a<b is true on top of everything, eg.
if a<b:
print(a)
n-=1
while using the syntax trick above.
In C, (n/n--)-1 is not only equal to 0, but also substracts 1 from n. In Python, I haven't found a way to do this. Some invalid syntaxes I tried:
print(("",a+(n/n--)-1)[a<b])
(print(""),(print(a);n-=1))[a<b]
How to decrement the variable (and print a) when the condition is true using this "trick"?
1,2,3: these statements aren't always true
Python isn't C. For one thing, Python doesn't have a decrement operator, so print(n--) won't work. For another, assignments in Python are statements, not expressions, so print(n-=1) won't work.
If you truly wanted your print statement to have side effects, it could invoke a function:
def decrement():
global n
n -= 1
return n
print(decrement())
But don't. No one will expect that your print statement has side-effects, so everyone will be surprised when commenting out your print statement changes the program's result.
EDIT: I just noticed that this is a code golf question. In that case, my stylistic advice isn't really valid. Everyone expects golfed code to be weird.
Ps. If your goal is to change if statements into expressions, then play with and and or, which short circuit. For example:
a<b and (print(a), decrement())
Or use if ... else expressions
(print(a),decrement()) if a<b else None
n = 10
print(n // (n := n-1) - 1)
# (x := y) is the equivalent of x = y
#but you can use it inside expressions
#and it returns the new value of x
# finally a C simple assignment
a = (b := 1)
print(a, b)
gives
0
1 1

Why is "1000000000000000 in range(1000000000000001)" so fast in Python 3?

It is my understanding that the range() function, which is actually an object type in Python 3, generates its contents on the fly, similar to a generator.
This being the case, I would have expected the following line to take an inordinate amount of time because, in order to determine whether 1 quadrillion is in the range, a quadrillion values would have to be generated:
1_000_000_000_000_000 in range(1_000_000_000_000_001)
Furthermore: it seems that no matter how many zeroes I add on, the calculation more or less takes the same amount of time (basically instantaneous).
I have also tried things like this, but the calculation is still almost instant:
# count by tens
1_000_000_000_000_000_000_000 in range(0,1_000_000_000_000_000_000_001,10)
If I try to implement my own range function, the result is not so nice!
def my_crappy_range(N):
i = 0
while i < N:
yield i
i += 1
return
What is the range() object doing under the hood that makes it so fast?
Martijn Pieters's answer was chosen for its completeness, but also see abarnert's first answer for a good discussion of what it means for range to be a full-fledged sequence in Python 3, and some information/warning regarding potential inconsistency for __contains__ function optimization across Python implementations. abarnert's other answer goes into some more detail and provides links for those interested in the history behind the optimization in Python 3 (and lack of optimization of xrange in Python 2). Answers by poke and by wim provide the relevant C source code and explanations for those who are interested.
The Python 3 range() object doesn't produce numbers immediately; it is a smart sequence object that produces numbers on demand. All it contains is your start, stop and step values, then as you iterate over the object the next integer is calculated each iteration.
The object also implements the object.__contains__ hook, and calculates if your number is part of its range. Calculating is a (near) constant time operation *. There is never a need to scan through all possible integers in the range.
From the range() object documentation:
The advantage of the range type over a regular list or tuple is that a range object will always take the same (small) amount of memory, no matter the size of the range it represents (as it only stores the start, stop and step values, calculating individual items and subranges as needed).
So at a minimum, your range() object would do:
class my_range:
def __init__(self, start, stop=None, step=1, /):
if stop is None:
start, stop = 0, start
self.start, self.stop, self.step = start, stop, step
if step < 0:
lo, hi, step = stop, start, -step
else:
lo, hi = start, stop
self.length = 0 if lo > hi else ((hi - lo - 1) // step) + 1
def __iter__(self):
current = self.start
if self.step < 0:
while current > self.stop:
yield current
current += self.step
else:
while current < self.stop:
yield current
current += self.step
def __len__(self):
return self.length
def __getitem__(self, i):
if i < 0:
i += self.length
if 0 <= i < self.length:
return self.start + i * self.step
raise IndexError('my_range object index out of range')
def __contains__(self, num):
if self.step < 0:
if not (self.stop < num <= self.start):
return False
else:
if not (self.start <= num < self.stop):
return False
return (num - self.start) % self.step == 0
This is still missing several things that a real range() supports (such as the .index() or .count() methods, hashing, equality testing, or slicing), but should give you an idea.
I also simplified the __contains__ implementation to only focus on integer tests; if you give a real range() object a non-integer value (including subclasses of int), a slow scan is initiated to see if there is a match, just as if you use a containment test against a list of all the contained values. This was done to continue to support other numeric types that just happen to support equality testing with integers but are not expected to support integer arithmetic as well. See the original Python issue that implemented the containment test.
* Near constant time because Python integers are unbounded and so math operations also grow in time as N grows, making this a O(log N) operation. Since it’s all executed in optimised C code and Python stores integer values in 30-bit chunks, you’d run out of memory before you saw any performance impact due to the size of the integers involved here.
The fundamental misunderstanding here is in thinking that range is a generator. It's not. In fact, it's not any kind of iterator.
You can tell this pretty easily:
>>> a = range(5)
>>> print(list(a))
[0, 1, 2, 3, 4]
>>> print(list(a))
[0, 1, 2, 3, 4]
If it were a generator, iterating it once would exhaust it:
>>> b = my_crappy_range(5)
>>> print(list(b))
[0, 1, 2, 3, 4]
>>> print(list(b))
[]
What range actually is, is a sequence, just like a list. You can even test this:
>>> import collections.abc
>>> isinstance(a, collections.abc.Sequence)
True
This means it has to follow all the rules of being a sequence:
>>> a[3] # indexable
3
>>> len(a) # sized
5
>>> 3 in a # membership
True
>>> reversed(a) # reversible
<range_iterator at 0x101cd2360>
>>> a.index(3) # implements 'index'
3
>>> a.count(3) # implements 'count'
1
The difference between a range and a list is that a range is a lazy or dynamic sequence; it doesn't remember all of its values, it just remembers its start, stop, and step, and creates the values on demand on __getitem__.
(As a side note, if you print(iter(a)), you'll notice that range uses the same listiterator type as list. How does that work? A listiterator doesn't use anything special about list except for the fact that it provides a C implementation of __getitem__, so it works fine for range too.)
Now, there's nothing that says that Sequence.__contains__ has to be constant time—in fact, for obvious examples of sequences like list, it isn't. But there's nothing that says it can't be. And it's easier to implement range.__contains__ to just check it mathematically ((val - start) % step, but with some extra complexity to deal with negative steps) than to actually generate and test all the values, so why shouldn't it do it the better way?
But there doesn't seem to be anything in the language that guarantees this will happen. As Ashwini Chaudhari points out, if you give it a non-integral value, instead of converting to integer and doing the mathematical test, it will fall back to iterating all the values and comparing them one by one. And just because CPython 3.2+ and PyPy 3.x versions happen to contain this optimization, and it's an obvious good idea and easy to do, there's no reason that IronPython or NewKickAssPython 3.x couldn't leave it out. (And in fact, CPython 3.0-3.1 didn't include it.)
If range actually were a generator, like my_crappy_range, then it wouldn't make sense to test __contains__ this way, or at least the way it makes sense wouldn't be obvious. If you'd already iterated the first 3 values, is 1 still in the generator? Should testing for 1 cause it to iterate and consume all the values up to 1 (or up to the first value >= 1)?
Use the source, Luke!
In CPython, range(...).__contains__ (a method wrapper) will eventually delegate to a simple calculation which checks if the value can possibly be in the range. The reason for the speed here is we're using mathematical reasoning about the bounds, rather than a direct iteration of the range object. To explain the logic used:
Check that the number is between start and stop, and
Check that the stride value doesn't "step over" our number.
For example, 994 is in range(4, 1000, 2) because:
4 <= 994 < 1000, and
(994 - 4) % 2 == 0.
The full C code is included below, which is a bit more verbose because of memory management and reference counting details, but the basic idea is there:
static int
range_contains_long(rangeobject *r, PyObject *ob)
{
int cmp1, cmp2, cmp3;
PyObject *tmp1 = NULL;
PyObject *tmp2 = NULL;
PyObject *zero = NULL;
int result = -1;
zero = PyLong_FromLong(0);
if (zero == NULL) /* MemoryError in int(0) */
goto end;
/* Check if the value can possibly be in the range. */
cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT);
if (cmp1 == -1)
goto end;
if (cmp1 == 1) { /* positive steps: start <= ob < stop */
cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE);
cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT);
}
else { /* negative steps: stop < ob <= start */
cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE);
cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT);
}
if (cmp2 == -1 || cmp3 == -1) /* TypeError */
goto end;
if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */
result = 0;
goto end;
}
/* Check that the stride does not invalidate ob's membership. */
tmp1 = PyNumber_Subtract(ob, r->start);
if (tmp1 == NULL)
goto end;
tmp2 = PyNumber_Remainder(tmp1, r->step);
if (tmp2 == NULL)
goto end;
/* result = ((int(ob) - start) % step) == 0 */
result = PyObject_RichCompareBool(tmp2, zero, Py_EQ);
end:
Py_XDECREF(tmp1);
Py_XDECREF(tmp2);
Py_XDECREF(zero);
return result;
}
static int
range_contains(rangeobject *r, PyObject *ob)
{
if (PyLong_CheckExact(ob) || PyBool_Check(ob))
return range_contains_long(r, ob);
return (int)_PySequence_IterSearch((PyObject*)r, ob,
PY_ITERSEARCH_CONTAINS);
}
The "meat" of the idea is mentioned in the comment lines:
/* positive steps: start <= ob < stop */
/* negative steps: stop < ob <= start */
/* result = ((int(ob) - start) % step) == 0 */
As a final note - look at the range_contains function at the bottom of the code snippet. If the exact type check fails then we don't use the clever algorithm described, instead falling back to a dumb iteration search of the range using _PySequence_IterSearch! You can check this behaviour in the interpreter (I'm using v3.5.0 here):
>>> x, r = 1000000000000000, range(1000000000000001)
>>> class MyInt(int):
... pass
...
>>> x_ = MyInt(x)
>>> x in r # calculates immediately :)
True
>>> x_ in r # iterates for ages.. :(
^\Quit (core dumped)
To add to Martijn’s answer, this is the relevant part of the source (in C, as the range object is written in native code):
static int
range_contains(rangeobject *r, PyObject *ob)
{
if (PyLong_CheckExact(ob) || PyBool_Check(ob))
return range_contains_long(r, ob);
return (int)_PySequence_IterSearch((PyObject*)r, ob,
PY_ITERSEARCH_CONTAINS);
}
So for PyLong objects (which is int in Python 3), it will use the range_contains_long function to determine the result. And that function essentially checks if ob is in the specified range (although it looks a bit more complex in C).
If it’s not an int object, it falls back to iterating until it finds the value (or not).
The whole logic could be translated to pseudo-Python like this:
def range_contains (rangeObj, obj):
if isinstance(obj, int):
return range_contains_long(rangeObj, obj)
# default logic by iterating
return any(obj == x for x in rangeObj)
def range_contains_long (r, num):
if r.step > 0:
# positive step: r.start <= num < r.stop
cmp2 = r.start <= num
cmp3 = num < r.stop
else:
# negative step: r.start >= num > r.stop
cmp2 = num <= r.start
cmp3 = r.stop < num
# outside of the range boundaries
if not cmp2 or not cmp3:
return False
# num must be on a valid step inside the boundaries
return (num - r.start) % r.step == 0
If you're wondering why this optimization was added to range.__contains__, and why it wasn't added to xrange.__contains__ in 2.7:
First, as Ashwini Chaudhary discovered, issue 1766304 was opened explicitly to optimize [x]range.__contains__. A patch for this was accepted and checked in for 3.2, but not backported to 2.7 because "xrange has behaved like this for such a long time that I don't see what it buys us to commit the patch this late." (2.7 was nearly out at that point.)
Meanwhile:
Originally, xrange was a not-quite-sequence object. As the 3.1 docs say:
Range objects have very little behavior: they only support indexing, iteration, and the len function.
This wasn't quite true; an xrange object actually supported a few other things that come automatically with indexing and len,* including __contains__ (via linear search). But nobody thought it was worth making them full sequences at the time.
Then, as part of implementing the Abstract Base Classes PEP, it was important to figure out which builtin types should be marked as implementing which ABCs, and xrange/range claimed to implement collections.Sequence, even though it still only handled the same "very little behavior". Nobody noticed that problem until issue 9213. The patch for that issue not only added index and count to 3.2's range, it also re-worked the optimized __contains__ (which shares the same math with index, and is directly used by count).** This change went in for 3.2 as well, and was not backported to 2.x, because "it's a bugfix that adds new methods". (At this point, 2.7 was already past rc status.)
So, there were two chances to get this optimization backported to 2.7, but they were both rejected.
* In fact, you even get iteration for free with indexing alone, but in 2.3 xrange objects got a custom iterator.
** The first version actually reimplemented it, and got the details wrong—e.g., it would give you MyIntSubclass(2) in range(5) == False. But Daniel Stutzbach's updated version of the patch restored most of the previous code, including the fallback to the generic, slow _PySequence_IterSearch that pre-3.2 range.__contains__ was implicitly using when the optimization doesn't apply.
The other answers explained it well already, but I'd like to offer another experiment illustrating the nature of range objects:
>>> r = range(5)
>>> for i in r:
print(i, 2 in r, list(r))
0 True [0, 1, 2, 3, 4]
1 True [0, 1, 2, 3, 4]
2 True [0, 1, 2, 3, 4]
3 True [0, 1, 2, 3, 4]
4 True [0, 1, 2, 3, 4]
As you can see, a range object is an object that remembers its range and can be used many times (even while iterating over it), not just a one-time generator.
It's all about a lazy approach to the evaluation and some extra optimization of range.
Values in ranges don't need to be computed until real use, or even further due to extra optimization.
By the way, your integer is not such big, consider sys.maxsize
sys.maxsize in range(sys.maxsize) is pretty fast
due to optimization - it's easy to compare given integer just with min and max of range.
but:
Decimal(sys.maxsize) in range(sys.maxsize) is pretty slow.
(in this case, there is no optimization in range, so if python receives unexpected Decimal, python will compare all numbers)
You should be aware of an implementation detail but should not be relied upon, because this may change in the future.
TL;DR
The object returned by range() is actually a range object. This object implements the iterator interface so you can iterate over its values sequentially, just like a generator, list, or tuple.
But it also implements the __contains__ interface which is actually what gets called when an object appears on the right-hand side of the in operator. The __contains__() method returns a bool of whether or not the item on the left-hand side of the in is in the object. Since range objects know their bounds and stride, this is very easy to implement in O(1).
Due to optimization, it is very easy to compare given integers just with min and max range.
The reason that the range() function is so fast in Python3 is that here we use mathematical reasoning for the bounds, rather than a direct iteration of the range object.
So for explaining the logic here:
Check whether the number is between the start and stop.
Check whether the step precision value doesn't go over our number.
Take an example, 997 is in range(4, 1000, 3) because:
4 <= 997 < 1000, and (997 - 4) % 3 == 0.
Try x-1 in (i for i in range(x)) for large x values, which uses a generator comprehension to avoid invoking the range.__contains__ optimisation.
TLDR;
the range is an arithmetic series so it can very easily calculate whether the object is there. It could even get the index of it if it were list like really quickly.
__contains__ method compares directly with the start and end of the range

Ternary returns/assignments vs traditional if-else blocks

PEP 8 discourages the usage of compound statements, but I couldn't find anything about the advised usage of Python's ternary/conditional syntax. For example:
return x if n == 0 else y
i = x if n == 0 else y if n == 1 else z
Does there exist any convention concerning whether the above statements should be preferred to more traditional if/else blocks?
if n == 0:
return x
return y
if n == 0:
i = x
elif n == 1:
i = y
else:
i = z
The "traditional if/else blocks" should generally be preferred.
The only places where you'd still want to use the ternary operator is in places where the syntax requires an expression, e.g. within lambda expressions. Even there, only do it if the ternary expression is short and readable (of course, readability is subjective..)
You can always replace a lambda expression with a small function, but in places where you think that would make the code less readable, it is ok to use lambda expressions with short ternary operations.
I agree with shx2's comments.
Another consideration is testability... using the ternary expr places all the logic on a single line/expr. Basic line-base code coverage tools wouldn't then give a good read on the true coverage of your tests.
Using traditional is/else blocks gives a better reading.

What does `<>` mean in Python?

I'm trying to use in Python 3.3 an old library (dating from 2003!). When I import it, Python throws me an error because there are <> signs in the source file, e.g.:
if (cnum < 1000 and nnum <> 1000 and ntext[-1] <> "s":
...
I guess it's a now-abandoned sign in the language.
What exactly does it mean, and which (more recent) sign should I replace it with?
It means not equal to. It was taken from ABC (python's predecessor) see here:
x < y, x <= y, x >= y, x > y, x = y, x <> y, 0 <= d < 10
Order tests (<> means 'not equals')
I believe ABC took it from Pascal, a language Guido began programming with.
It has now been removed in Python 3. Use != instead. If you are CRAZY you can scrap != and allow only <> in Py3K using this easter egg:
>>> from __future__ import barry_as_FLUFL
>>> 1 != 2
File "<stdin>", line 1
1 != 2
^
SyntaxError: with Barry as BDFL, use '<>' instead of '!='
>>> 1 <> 2
True
It means NOT EQUAL, but it is deprecated, use != instead.
It's worth knowing that you can use Python itself to find documentation, even for punctuation mark operators that Google can't cope with.
>>> help("<>")
Comparisons
Unlike C, all comparison operations in Python have the same priority,
which is lower than that of any arithmetic, shifting or bitwise
operation. Also unlike C, expressions like a < b < c have the
interpretation that is conventional in mathematics:
Comparisons yield boolean values: True or False.
Comparisons can be chained arbitrarily, e.g., x < y <= z is
equivalent to x < y and y <= z, except that y is evaluated
only once (but in both cases z is not evaluated at all when x <
y is found to be false).
The forms <> and != are equivalent; for consistency with C,
!= is preferred; where != is mentioned below <> is also
accepted. The <> spelling is considered obsolescent.
See http://docs.python.org/2/reference/expressions.html#not-in
It is an old way of specifying !=, that was removed in Python 3. A library old enough to use it likely runs into various other incompatibilities with Python 3 as well: it is probably a good idea to run it through 2to3, which automatically changes this, among many other things.
Use != or <>. Both stands for not equal.
[Reference: Python language reference]
The comparison operators <> and != are alternate spellings of the same operator. != is the preferred spelling; <> is obsolescent.

Categories