This is so cursed. for those who don't get it, taking the negative of a two's complement (the common way to represent signed integers) integer is equivalent to "bitwise not, then increment" (try thinking about what -~i means, then, before I spoil it) what this is doing is taking the negative (-) of the bitwise not (~), resulting in "not, not, increment", the nots cancelling each other out. excercise for the reader: how would you construct "decrement" using this knowledge?
*_, is not a single operator but a modifier, a variable and an operator (in that order). The comma will unwrap the object right of the equals, the underscore will act like a variable, and I'm actually not sure of the asterisk. I guess it does the list conversion?
Python can assign a pattern on the right to a tuple on the left. The underscore is just the variable name, the asterisk takes anything no already captured and the comma makes it a one element tuple
I don't think the comma does anything, since it was in the context of where it's used to separate variables. The asterisk is the one that unwraps the list.
@@xaf15001 It's complicated. If you do 'x,*ˍ = ' it will put the 1st element of the iterable in x and all other elements in a list in 'ˍ'. Conversely, if you do '*ˍ, x = ', it will put the last element in x and all others in a list in 'ˍ'. But you can't do '*ˍ = '. That will produce an error that the left hand side must be a list or tuple. But '*ˍ, = ' makes it a tuple of 1 element, and puts all the elements of the iterable in a list in 'ˍ'. (Note: the right hand side can be any iterable: string, list, tuple, set, dict, generators, etc. Edited because YT uses asterisks and underscores as markup.
I really appreciate your coverage of the full range of Python functionality. It is very helpful. I would like to suggest that some of the “value judgment“ terminology may be less helpful. For example, to someone who is very comfortable with the syntax pattern, the appearance of //= is not “weird”. It is still fair (IMHO) to offer the caveat that it is less commonly used, and therefore may slow down readers of the code who need to stop and figure it out.
That is sick. I assume you can do more than just first and last? Something like >>> first, second, *_, beforeLast, last = "Python" should be possible I reckon?
I remember a time when Python did not have //, even before we got it from __future__. Python did different things based on the type of numbers back then, which again made sense if you used other lower-level languages but what a pain in the arse to explain to someone new. The // floor-division operator provides the effect of integer division (discard the remainder, return an int) in a way that was more consistent and leave the normal / to do "normal" division. Again, this stuff tends to make sense when you're doing bitwise operations, and while I do those in Python, I'm not surprised if others don't. That last one… 😬 It WORKS, but if you use it as anything other than this video, yikes.
This is interesting! It means you can use: --------------------------------------------------------0 ~~~~~~~~~~~~~~~~~~~~~~~0 ++++++++++++++++++++++++++++0 as separators without causing any errors
You really should introduce people to the awesome f-string which makes operations clear ... Like instead of print(a-b), do this instead: print(f"{a - b = }") And so on.
wouldnt call the operators crazy and i knew most of them but some py shinanigans like the last one where new to me so i would call the video informative. well done keep it up.
We've come a long way since… from operator import mul print(sum(map(mul, A, B))) 😁 (There isn't a convenient operator for dot products that I know of.) Dot and cross products are used a lit in 3D stuff, which is the major reason I learned any of that.
The difference between `var = list(iterable)` and `*var, = iterable` is that the `*var` approach is around 10ns faster (Python 3.12). Definitely not worth it. Stay with `list(var)`.
Really? In python 3.12 using timeit I consistently got about ~180ns for “*x,” and ~140 for list(x) or about 40 ns faster for the list approach. Doesn’t really change the takeaway but still.
@@MrMoon-hy6pn Yeah, not sure why we're seeing different results on 3.12. I've now re-tested using Python 3.13, and `list(x)` is indeed around 40ns faster than `*x`. I guess that settles it🙂
Since you asked here's a list of (American English) names for some of the weird characters that came up: ~ tilde (til duh) (you got this one right) & ampersand @ commat (comm at) ^ carat (pronounced "carrot") Side note: concerning the "pistol operator", I like to call it the "spread operator", which I got from JavaScript but I'm pretty sure is used in several other languages. It takes the individual elements of an iterable and "spreads them out."
I wouldn't say "commat" is the most common name for @; I'd say that's jargon only familiar in certain fields. It's short for "commercial at", which is probably a little more standard, but in my experience by far the most common name is simply "at sign".
Saying "pipe" instead of "or" is, IMO, doing a disservice to newbies to Python. | = or - = without / subtract ^ = not & = and The symbols are the same as the bit-wise operands, but they act like their logical variants which are words in Python (or, and, not - the minus is a weird outlier).
One important thing to know is that normally these operators (~ | & ^) work on the individual bits of a variable; the reason they also work on other objects in Python is due to "operator overloading," meaning the same symbol and operation are maintained but applied to different objects. To better understand this, one should study a bit of C++. If transitioning from Python to other languages, it's important to remember that ~ is different from !, even though both are "not," but ! performs an implicit conversion to bool; the same applies to & and &&. I think it's better to learn certain things from other languages rather than Python.
int(number) returns the number truncated to a whole number. A float number is represented the same way when it is positive or negative, just with a sign bit being different-so we get truncation towards zero: int(-1.1)=-1, int(-0.1)=0, int(0.1)=0, int(1.1)=1. The // is best understood together with % (modulo operator): for two numbers, a and b, we always want a = (a // b) * b + (a % b) to hold and we always want (a % b) to be a non-negative number smaller than b. Therefore, if a = -11 and b = 3, we have no choice but to write it as a = -4*3 + 1.
Absolutely not. Python interprets things in context. You use ( ) for both function definitions and tuples, and it's not an issue. Likewise, the set operators also do bitwise operations, IN CONTEXT. 😁
Python's parser understands the difference between @ when used as a binary infix operator, and @ when it is a prefix of a name in the global scope decorating a function definiton. The @ symbol is in fact a binary operator at all times in python, there's just no default behaviour for it. You can create behaviour for it by implementing the __matmul__ method in your classes.
Used it just yesterday to code a custom base conversion function from integers to strings, very useful to loop through the steps of a euclidean division!
Yes, he ignored the leading zeros which really confuses the explanation. For those unfamiliar: Internally the int is represented by a fixed number of bits, regardless of how big it is (or, more accurately, it will automatically choose from a set of predetermined int sizes depending on the value). When you do bitwise negation, that affects all bits, including the leading zeros. Since Python (like most programming languages) uses two's complement, the most significant bit represents whether the number is positive (0) or negative (1). The reason why flipping 5 becomes -6 (rather than -5) is because it simplifies the internal workings of integer addition, and removes what would otherwise be a resundant double representation of zero - this is called one's complement, and it's less useful. One's complement works like this: positive integers work like standard mathematical binary, and are denoted by the most significant bit being 0. Negative integers are simply their bitwise negation (i.e. every bit is flipped to its opposite value. So, for 8-bit integers: 00000101 = 5 11111010 = -5 This divides the space perfectly equally between positive and negative integers, but it leaves two different representations of zero: 00000000 = 0 11111111 = -0 There can be contexts where negative zero is a useful concept, but that's a little esoteric for most uses. Also, a naïve implementation of addition would have -0 + 1 = 0: 11111111 + 00000001 = 00000000 (overflow) Two's complement works almost the same way, except the value of each negative integer is one less than what it would be in one's complement: 11111111 = -1 11111010 = -6 This way there is only one representation of 0, and the addition logic can be very simple. The only slightly odd effect is that, because zero takes up a spot in the positive half, the lowest representable negative value is greater in magnitude (by one) than the highest representable positive value. That doesn't really matter, but it _feels_ a bit peculiar!
@@TheJamesMBasically correct, but there is a problem. The integer does not have -0. The reason why 0b1...1010 is -6 is that when added to 6 (0b0...0110), it will be 0. 0b11...1010 (x) +0b00...0110 (+6) =0b00...0000 (=0)
@@Jason-b9t Sorry, I wasn't being very clear. I was specifically saying that that's what it _doesn't_ do (i.e. two's complement doesn't have -0). 0b000... and 0b111... at the end of my message are saying how it works in one's complement. I'll edit to clarify.
The // operator does not always round down, that's a confusing way of putting it. Rather it truncates towards zero for positives and away from zero for negatives. For example, say the result of a division with a single / is -4.66657585. If instead we used //, the result would be -5, not -4.
@@NishithSavla in pure mathematics terms yes. But a lot of people forget that negative numbers are in "reverse" so they tend to just discard the decimal and keep the number which is not correct
r’your regex here’ (r for raw. Reason for using this is so that you don’t need to escape your backslashes in order to use them as escape codes in regex) and the regex library re.match etc. I don’t know why I watched this video… I guess it was useful to learn that the dunder method for @ is matmul, but like, I could have quickly looked that up if I wanted to use it. I guess I was procrastinating by watching the video. If you want to know how to use regex in Python, you are best off reading the docs imo, not waiting for a channel to release a tutorial on it.
The wrong script run, as always (relatable)
Python Developer:
There's also the successor operator -~ with -~0 = 1, -~1 = 2, ...
That's incredible, thanks for sharing this!
This is so cursed.
for those who don't get it, taking the negative of a two's complement (the common way to represent signed integers) integer is equivalent to "bitwise not, then increment"
(try thinking about what -~i means, then, before I spoil it)
what this is doing is taking the negative (-) of the bitwise not (~), resulting in "not, not, increment", the nots cancelling each other out.
excercise for the reader: how would you construct "decrement" using this knowledge?
and the predecessor operator ~-
*_, is not a single operator but a modifier, a variable and an operator (in that order). The comma will unwrap the object right of the equals, the underscore will act like a variable, and I'm actually not sure of the asterisk. I guess it does the list conversion?
The asterisk returns (x for x in "Python")
Python can assign a pattern on the right to a tuple on the left. The underscore is just the variable name, the asterisk takes anything no already captured and the comma makes it a one element tuple
I don't think the comma does anything, since it was in the context of where it's used to separate variables. The asterisk is the one that unwraps the list.
@@xaf15001comma is the "create a tuple" operator
@@xaf15001 It's complicated. If you do 'x,*ˍ = ' it will put the 1st element of the iterable in x and all other elements in a list in 'ˍ'. Conversely, if you do '*ˍ, x = ', it will put the last element in x and all others in a list in 'ˍ'. But you can't do '*ˍ = '. That will produce an error that the left hand side must be a list or tuple. But '*ˍ, = ' makes it a tuple of 1 element, and puts all the elements of the iterable in a list in 'ˍ'.
(Note: the right hand side can be any iterable: string, list, tuple, set, dict, generators, etc.
Edited because YT uses asterisks and underscores as markup.
My personal projects grow more esoteric and illegible by the day
I really appreciate your coverage of the full range of Python functionality. It is very helpful.
I would like to suggest that some of the “value judgment“ terminology may be less helpful. For example, to someone who is very comfortable with the syntax pattern, the appearance of //= is not “weird”. It is still fair (IMHO) to offer the caveat that it is less commonly used, and therefore may slow down readers of the code who need to stop and figure it out.
`*_,` is useful when combined with other variables in the unpacking. e.g.
>>> first, *_, last = "Python"
>>> first, last
('P', 'n')
That is sick. I assume you can do more than just first and last? Something like
>>> first, second, *_, beforeLast, last = "Python"
should be possible I reckon?
the ^ operator works like xor for integers, which i use so many times for data science
4:55 unary bitwise complement; its dunder is __invert__
~ is indeed pronounced "tilde" 😊
Well it’s spelled that way, but pronounced “till duh”
@@chixenlegjono, it's tilda, not tildeh or tilde
/ˈtɪldə,ˈtɪldi/
Learn this for job security 🏃♂️
I remember a time when Python did not have //, even before we got it from __future__. Python did different things based on the type of numbers back then, which again made sense if you used other lower-level languages but what a pain in the arse to explain to someone new. The // floor-division operator provides the effect of integer division (discard the remainder, return an int) in a way that was more consistent and leave the normal / to do "normal" division. Again, this stuff tends to make sense when you're doing bitwise operations, and while I do those in Python, I'm not surprised if others don't.
That last one… 😬 It WORKS, but if you use it as anything other than this video, yikes.
This is interesting! It means you can use:
--------------------------------------------------------0
~~~~~~~~~~~~~~~~~~~~~~~0
++++++++++++++++++++++++++++0
as separators without causing any errors
some of these can be used for non-maintainable code
Not everything has to be demure darling, and you should be able to look at a @ b and know what it does
All of these can be used for non-maintainable code. The cool ones can't be used for maintainable code
I like to call the "//" the ✨smooth operator ✨
Tilde is correct regardless of whether you pronounce the e.
And I always follow the rule that correct is in the ear of the listener.
The floor division result is the quotient of the euclidian division. That's how I learned it
"AM I ILITERATE?!"
Somedays i ask myself the same question...
This operations on sets was in my university tests at cybernetics. I never used them to write my scripts but should to learn by hard for exam
For data wrangling I use ~ all the time!
7:56 when writing a cmd simulator. `cmd, *args = "call a b c".split(' ')`
Pistol operator is now my favourite
You really should introduce people to the awesome f-string which makes operations clear ...
Like instead of print(a-b), do this instead:
print(f"{a - b = }")
And so on.
slight syntax error: f"a - b = {a-b}" (the part in {} is evaluated)
@@danik0011 it’s not a syntax error. f”{a - b = }” is equivalent to f”a - b = {a - b}”
"But the benefit of using this approach is that it gives you this funny face back... that kind of looks like eh" @ 4:42. Lol
wouldnt call the operators crazy and i knew most of them but some py shinanigans like the last one where new to me so i would call the video informative. well done keep it up.
Man, that @ (at) operator took me by surprise! 😆
The @ operator is not a standard Python operator but is introduced only by NumPy. Therefore also in my opinion it doesn't count.
We've come a long way since…
from operator import mul
print(sum(map(mul, A, B)))
😁 (There isn't a convenient operator for dot products that I know of.)
Dot and cross products are used a lit in 3D stuff, which is the major reason I learned any of that.
4:53 you got it correct, it is called a tilda good job (i don’t know how to spell it i think thats right)
tilde
The last one looks like a Morse code
Personally, I like weird esoteric constructions. Have you heard of the IOCCC? Maybe there should be an IOPCC.
The difference between `var = list(iterable)` and `*var, = iterable` is that the `*var` approach is around 10ns faster (Python 3.12).
Definitely not worth it. Stay with `list(var)`.
Really? In python 3.12 using timeit I consistently got about ~180ns for “*x,” and ~140 for list(x) or about 40 ns faster for the list approach. Doesn’t really change the takeaway but still.
@@MrMoon-hy6pn Yeah, not sure why we're seeing different results on 3.12.
I've now re-tested using Python 3.13, and `list(x)` is indeed around 40ns faster than `*x`. I guess that settles it🙂
~ operator have cool combination with -
~-a is equal to a-1
and -~a is a+1
They are operator sperm right and left. (because of the tail)
Love the last one!
Since you asked here's a list of (American English) names for some of the weird characters that came up:
~ tilde (til duh) (you got this one right)
& ampersand
@ commat (comm at)
^ carat (pronounced "carrot")
Side note: concerning the "pistol operator", I like to call it the "spread operator", which I got from JavaScript but I'm pretty sure is used in several other languages. It takes the individual elements of an iterable and "spreads them out."
I wouldn't say "commat" is the most common name for @; I'd say that's jargon only familiar in certain fields. It's short for "commercial at", which is probably a little more standard, but in my experience by far the most common name is simply "at sign".
Do you use the walrus operator?
Super useful in if statements so you don’t have assign a value twice.
Waaaah such operators, it looks like my cat coding with my computer
the last one made me lose my job, ty
~ is the squiggle, everyone knows that
Saying "pipe" instead of "or" is, IMO, doing a disservice to newbies to Python.
| = or
- = without / subtract
^ = not
& = and
The symbols are the same as the bit-wise operands, but they act like their logical variants which are words in Python (or, and, not - the minus is a weird outlier).
Same for |= being an or operator for the keys in the dict.
I’d say XOR rather than “not” for ^
~ is not
^ is xor
One important thing to know is that normally these operators (~ | & ^) work on the individual bits of a variable; the reason they also work on other objects in Python is due to "operator overloading," meaning the same symbol and operation are maintained but applied to different objects. To better understand this, one should study a bit of C++.
If transitioning from Python to other languages, it's important to remember that ~ is different from !, even though both are "not," but ! performs an implicit conversion to bool; the same applies to & and &&.
I think it's better to learn certain things from other languages rather than Python.
^ = XOR because it gives the set where elements are (in A) XOR (in B)
Wait, what? How long has there been a matmul dunder bound to 🐌?
As far as I know, as long as you import numpy, it's been there for quite a while!
It's available since Python 3.5. Initially, literally nothing used it, as it's completely unused in the standard library.
interesting that -11//3 = -4 and int(-11/3)=-3 ..
int(number) returns the number truncated to a whole number. A float number is represented the same way when it is positive or negative, just with a sign bit being different-so we get truncation towards zero: int(-1.1)=-1, int(-0.1)=0, int(0.1)=0, int(1.1)=1. The // is best understood together with % (modulo operator): for two numbers, a and b, we always want a = (a // b) * b + (a % b) to hold and we always want (a % b) to be a non-negative number smaller than b. Therefore, if a = -11 and b = 3, we have no choice but to write it as a = -4*3 + 1.
The clue is in the name: "Floor-division":
>>> from math import floor
>>> floor(-11/3)
-4
floor() returns the largest integral (integer) value
Thank you very much for your videos
Worst operator thing I've ever seen was in C, and it looked something like `a++ + ++b`
Thank you so much!
The "correct" way to use the "pistol operator" is to not use it and to write
x = list(y)
instead of
*x, = y
7:53
4:55 yeah, thats a tilde
and a virgulilla
Well, how the matrix mult @ will work together with the same @ decorator symbol in the same program? Will that generate an error?
Absolutely not. Python interprets things in context. You use ( ) for both function definitions and tuples, and it's not an issue. Likewise, the set operators also do bitwise operations, IN CONTEXT. 😁
Python's parser understands the difference between @ when used as a binary infix operator, and @ when it is a prefix of a name in the global scope decorating a function definiton.
The @ symbol is in fact a binary operator at all times in python, there's just no default behaviour for it. You can create behaviour for it by implementing the __matmul__ method in your classes.
Thanks!
I've been using python for ages and I'd never seen this before!
@@indigojones4442doesn’t have to be in global scope though?
Thank you 😊
11:09 No! This is Morse code haha
What is the reference to these operators??
I take it you don’t do much bit manipulation
wrong script error is starting to become a running gag 😅
I use //= quite often actually!
Used it just yesterday to code a custom base conversion function from integers to strings, very useful to loop through the steps of a euclidean division!
5:24 But doesn't flipping it like that result to 10 instead of -6?
I suspect Python is interpreting numbers as Two's complement binary, that's why you get negative numbers.
as it's a signed integer, no. google "twos complement".
Yes, he ignored the leading zeros which really confuses the explanation.
For those unfamiliar: Internally the int is represented by a fixed number of bits, regardless of how big it is (or, more accurately, it will automatically choose from a set of predetermined int sizes depending on the value). When you do bitwise negation, that affects all bits, including the leading zeros. Since Python (like most programming languages) uses two's complement, the most significant bit represents whether the number is positive (0) or negative (1). The reason why flipping 5 becomes -6 (rather than -5) is because it simplifies the internal workings of integer addition, and removes what would otherwise be a resundant double representation of zero - this is called one's complement, and it's less useful.
One's complement works like this: positive integers work like standard mathematical binary, and are denoted by the most significant bit being 0. Negative integers are simply their bitwise negation (i.e. every bit is flipped to its opposite value. So, for 8-bit integers:
00000101 = 5
11111010 = -5
This divides the space perfectly equally between positive and negative integers, but it leaves two different representations of zero:
00000000 = 0
11111111 = -0
There can be contexts where negative zero is a useful concept, but that's a little esoteric for most uses. Also, a naïve implementation of addition would have -0 + 1 = 0:
11111111 + 00000001 = 00000000 (overflow)
Two's complement works almost the same way, except the value of each negative integer is one less than what it would be in one's complement:
11111111 = -1
11111010 = -6
This way there is only one representation of 0, and the addition logic can be very simple. The only slightly odd effect is that, because zero takes up a spot in the positive half, the lowest representable negative value is greater in magnitude (by one) than the highest representable positive value. That doesn't really matter, but it _feels_ a bit peculiar!
@@TheJamesMBasically correct, but there is a problem. The integer does not have -0. The reason why 0b1...1010 is -6 is that when added to 6 (0b0...0110), it will be 0.
0b11...1010 (x)
+0b00...0110 (+6)
=0b00...0000 (=0)
@@Jason-b9t Sorry, I wasn't being very clear. I was specifically saying that that's what it _doesn't_ do (i.e. two's complement doesn't have -0). 0b000... and 0b111... at the end of my message are saying how it works in one's complement. I'll edit to clarify.
I learnt language paytho and made some project hop all see and give me werit
Assume
The // operator does not always round down, that's a confusing way of putting it. Rather it truncates towards zero for positives and away from zero for negatives. For example, say the result of a division with a single / is -4.66657585. If instead we used //, the result would be -5, not -4.
That's what rounding downwards means right?!
@@NishithSavla in pure mathematics terms yes. But a lot of people forget that negative numbers are in "reverse" so they tend to just discard the decimal and keep the number which is not correct
That's literally how "floor division" works.
@@enzozbestetti5992 that's called a skill issue man.
That's literally what it does. It always rounds down. If a person doesn't understand, that -5 is smaller number than -4, then that's their problem.
floor division operator is odd since // in many languages signifies a comment.
Maybe one-day you'll teach us how-to use regular expressions in python?
r’your regex here’ (r for raw. Reason for using this is so that you don’t need to escape your backslashes in order to use them as escape codes in regex) and the regex library
re.match etc.
I don’t know why I watched this video… I guess it was useful to learn that the dunder method for @ is matmul, but like, I could have quickly looked that up if I wanted to use it.
I guess I was procrastinating by watching the video.
If you want to know how to use regex in Python, you are best off reading the docs imo, not waiting for a channel to release a tutorial on it.
That was fun😂😂
Funny one
First!