Today I learned that Python 3.5+ supports the operator `@`

for matrix multiplication.

`@`

Since Python 3.5, Python has the infix operator `@`

.
This operator was introduced with PEP 465 to be used in matrix multiplication.

You can try to use it with just vanilla Python,
but no vanilla Python types define their behaviour with `@`

:

```
>>> 3 @ 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for @: 'int' and 'int'
```

However, just looking at the error above, you see that the error is in `@`

not knowing what to do with integers.
The error *is not* the fact that `@`

is an invalid operator!
So cool!

`numpy`

with `@`

If you have `numpy`

at hand, you can check `@`

works,
because `numpy`

arrays added support to be used with `@`

:

```
>>> import numpy as np
>>> np.random.rand(3, 3) @ np.random.rand(3, 3)
array([[0.89431673, 0.57949659, 0.59470797],
[0.47364302, 0.29837518, 0.33552972],
[1.12634752, 0.75218169, 0.78876082]])
>>> _ @ np.eye(3) # The identity (eye) matrix leaves the other matrix unchanged.
array([[0.89431673, 0.57949659, 0.59470797],
[0.47364302, 0.29837518, 0.33552972],
[1.12634752, 0.75218169, 0.78876082]])
```

`_`

is just a way to refer to the last result of the REPL.
Read about it in this Pydon't.

`@`

with custom classes/typesIf you want your own objects to add support for `@`

,
all you have to do is implement the dunder methods `__matmul__`

and `__rmatmul__`

:

```
>>> class Dummy:
... def __matmul__(self, other):
... print("Works!")
... return 42
... def __rmatmul__(self, other):
... print("Also works!")
... return 73
...
>>> d = Dummy()
>>> d @ 1
Works!
42
>>> 1 @ d
Also works!
73
```

There's also the `__imatmul__`

method for in-place matrix multiplication:

```
>>> class Dummy:
... def __imatmul__(self, other):
... print("In-place!")
...
>>> d = Dummy()
>>> d @= 1
In-place!
```

Of course, this silly example above doesn't show you the proper semantics of the `__matmul__`

,
`__rmatmul__`

, and `__imatmul__`

methods.
It just shows you they exist and they interact with the operator `@`

!

By the way, for reference, here is the tweet that showed me this:

How did I not know @ was a proper operator?!

— Rodrigo 🐍📝 (@mathsppblog) October 12, 2021

That's it for now! Stay tuned and I'll see you around!

]]>Today I learned that you can use emojis as variable names in Python if you use `pythonji`

.

No! At the time of writing, emojis are not valid Python identifiers. This means that this code fails:

`>>> 🍔 = "hamburguer" # SyntaxError`

However, if you install the package
`pythonji`

,
you will be able to run code like that!

Installing `pythonji`

is as easy as `python -m pip install pythonji`

!

With `pythonji`

installed, we can run programs that make use of emojis!

Here's a little program I wrote:

```
import enum
class 🍽(enum.Enum):
🍔 = "hamburguer"
🍕 = "pizza"
🍅 = "tomato"
🥕 = "carrot"
class 🧍:
def __init__(🤳, 😋👍):
🤳.😋👍 = 😋👍
def 🍽(🤳, 😋):
if 😋 in 🤳.😋👍:
return "Yummi!"
else:
return "Ok, I'll eat that."
👨 = 🧍([🍽.🍕, 🍽.🥕])
print(👨.🍽(🍽.🍕))
print(👨.🍽(🍽.🍅))
```

Save it to the file `foo.🐍`

(yes, the extension really is 🐍!).

Now, run it with `pythonji foo.🐍`

and this is the output:

```
> pythonji foo.🐍
Yummi!
Ok, I'll eat that.
```

Amazing, right? 😆

That's it for now! Stay tuned and I'll see you around!

]]>Today I learned that Python generators can return a value.

Generators are interesting Python objects that produce a series of values, but one by one. In a way, they can be thought of as stateful functions. (That is, functions with state.)

What I learned is that generators can also return something. Here is the tweet that prompted this discovery:

#Python pop quiz: Is this code valid? If so, what does it do?

— Raymond Hettinger (@raymondh) October 7, 2021

def f():

yield 10

return 20

g = f()

print(next(g))

print(next(g))

When you use a `return`

inside a generator,
the generator will have that returned information in its `StopIteration`

exception when it's done:

```
>>> def f():
... yield 10
... return 20
...
>>> gen = f()
>>> next(gen)
10
>>> next(gen)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration: 20
```

If you want to get access to that value, you just need to catch the exception:

```
>>> gen = f(); next(gen);
10
>>> try:
... next(gen)
... except StopIteration as e:
... val = e.value
...
>>> val
20
```

That's it for now! Stay tuned and I'll see you around!

]]>Today I learned about the built-in function `vars`

in Python.

`vars`

Python has a bunch of built-in functions. Like, a lot! It's very difficult to keep track of all of them, remember them, and use them correctly.

A built-in function I just learned about is `vars`

.
I heard about it from Reuven Lerner (a Python trainer) in his newsletter “Better Developers”.
(Disclaimer: that's a referral link, but I *am* a subscriber and avid reader of the newsletter,
so it is a very honest recommendation!)

Looking at the Python documentation, we can see what `vars`

does:

“Return the

`__dict__`

attribute for a module, class, instance, or any other object with a`__dict__`

attribute.”

The documentation also goes on to say that “without an argument, `vars()`

acts like `locals()`

.”.
So that's not useful because we can always use `locals()`

.

When `vars`

really shines is when you give it an argument, like a module or a class instance!

```
>>> class Person:
... def __init__(self, name):
... self.name = name
...
>>> p = Person("me")
>>> vars(p)
{'name': 'me'}
```

So, we can see that `vars`

is a very handy way of inspecting an instance of a class you defined.
Quite cool, right?

For things like built-in classes, or modules, `vars`

is similar to `dir`

.
Recall that `dir`

lists the names of all the attributes of an object,
but `vars`

will give you a mapping with the names of the attributes and the corresponding values:

```
>>> import math
>>> dir(math)
['__doc__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'comb', 'copysign', 'cos', 'cosh', 'degrees', 'dist', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'isqrt', 'lcm', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'nextafter', 'perm', 'pi', 'pow', 'prod', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc', 'ulp']
>>> vars(math)
{'__name__': 'math', '__doc__': 'This module provides access to the mathematical functions\ndefined by the C standard.', ...}
```

That's it for now! Stay tuned and I'll see you around!

]]>Consegues desenhar 4 triângulos nesta grelha 5 por 5 e cobrir todos os pontos?

O teu objetivo é desenhar 4 triângulos numa grelha 5 por 5. Claro que há algumas restrições:

- todos os vértices de todos os triângulos têm de estar sobre pontos da grelha;
- os 25 pontos da grelha têm de ser atravessados por uma aresta de algum triângulo, ou estar sob algum vértice; e
- os triângulos não podem ter ângulos de 90⁰ (por este motivo, concluímos que a imagem em cima não é parte da solução, já que o triângulo da direita tem um ângulo de 90⁰).

Pensa um pouco!

Se precisares de clarificar alguma coisa, não hesites em perguntar na secção de comentários em baixo.

Eu encontrei este problema no site [Puzzling][source] da família de sites do Stack Exchange.

Parabéns a todos os que conseguiram resolver o problema e, em particular, aos que me enviaram as suas soluções:

- David H., Taiwan;
- Michael W., Estados Unidos;
- Pedro G., Portugal;
- B. Praveen R., Índia;
- Kees L., Países Baixos;
- Jerry J., Estados Unidos;
- Mihalis G., Grécia;
- Alfredo E., México;

Junta-te à comunidade e envia-me a tua solução por email!

Uma proposta de solução vai ser publicada aqui quando tiverem passado duas semanas desde a publicação deste problema.

Não te esqueças de subscrever a newsletter para receberes os problemas diretamente na tua caixa de correio.

]]>Today I learned that the `.join`

method in Python is a two-pass algorithm, and that's why joining a list comprehension is faster than a generator expression.

My talk at EuroPython 2021 was finally uploaded to YouTube. I got really excited about this, so I decided to share it on Twitter:

🚨📢 The recording 📽 of my @EuroPython 2021 talk has been uploaded to YouTube!

— Rodrigo 🐍📝 (@mathsppblog) September 29, 2021

In it, I walk you through the refactoring of a function, introducing many Python features.

Go watch it, and let me know if you find the Easter egg! 🥚 No one has, yet!https://t.co/L3acpxN3mI

In my talk, I walk you through successive refactors of a piece of “bad” Python code, until you reach a “better” piece of Python code. The talk follows closely the “bite-sized refactoring” Pydont'.

In the talk, I started with this code:

```
def myfunc(a):
empty=[]
for i in range(len(a)):
if i%2==0:
empty.append(a[i].upper())
else:
empty.append(a[i].lower())
return "".join(empty)
# ---
>>> myfunc("Hello, world!")
'HeLlO, wOrLd!'
>>> myfunc("Spongebob.")
'SpOnGeBoB.'
```

And refactored it up until this point:

```
def alternate_casing(text):
return "".join([
char.lower() if idx % 2 else char.upper()
for idx, char in enumerate(text)
])
```

`.join`

In a feedback tweet, someone said that I could've removed the `[]`

of the list comprehension.
That would, instead, define a generator expression,
that should even be faster.

*Thankfully*, I was prepared for that:
before the talk, I checked the performance of both alternatives,
and found out that the generator expression was *not* faster.

That surprised me, because I was under the impression that generator expressions tend to be faster that list comprehensions, but the data in front of me didn't lie... And so, I didn't change the list comprehension into a generator expression in the talk...

But why is the generator expression slower?

As it turns out, the `.join`

method is a two-pass algorithm!

At least, according to a tweet that was quoted to me:

This is incorrect — due to the implementation of str.join, the list comprehension is faster (annoyingly) https://t.co/gOdJm9hrd0

— Alex Waygood (@AlexWaygood) September 29, 2021

Sadly, I haven't been able to verify this yet.

However, it does make some sense:
if `.join`

really is a two-pass algorithm, then generator expressions hurt us,
because generators can only be traversed once.
Therefore, we need to do something before `.join`

can actually start its work.

Peeking at the source code for `.join`

will probably reveal this...

I'm a bit sleepy, but I'll interrupt typing *right now* to take a quick peek at the source,
see if I can find this code.

[Me browsing through Python's source code.]

Ok, I think I found it 🎉🎉!

In Python's GitHub repo, there's an `Objects`

folder that contains a `stringlib`

that has a `join.h`

file.
Opening it, you can see that there are two `for`

loops over the data.

In the exact commit I looked at, at the beginning of the 30th of September of 2021,
those two `for`

loops start...

Today I learned about the `math.nextafter`

method.

`math.nextafter`

0 is a neat number, isn't it?

Perhaps one of the greatest discoveries of mankind.

But what's the number that comes after 0? That would be the smallest number in the set \(]0, +\inf[\), if you're familiar with the mathematical notation for sets.

In short, \([a, b]\) is the contiguous set of numbers \(x\) that satisfy the restriction \(a \leq x \leq b\). Notice how \(a\) and \(b\) are included inside \([a, b]\) because the brackets are closed. If the brackets are open, then that number is not included.

For the intervals below, \(x\) belongs to it if...

- \([a, b]\) → \(a \leq x \leq b\);
- \([a, b[\) → \(a \leq x < b\);
- \(]a, b]\) → \(a < x \leq b\); and
- \(]a, b[\) → \(a < x < b\).

So, in \(]0, +\infty[\), nor 0, nor \(+\infty\) are included. Thus, what's the minimum element of this interval? Well, there isn't any!

Mathematically speaking, there is no minimum in the interval \(]0, +\infty[\). Why not? Whatever you pick as a potential minimum \(m\), \(m/2\) will be smaller than \(m\) and still be greater than \(0\), that is, \(0 < m/2 < m < +\infty\), and so \(m/2\) is in \(]0, +\infty[\).

That's interesting, right?

But this is a whole other story if we go into the programming real!
Because of how floats are represented,
Python *has* a number that comes immediately after `0`

.
So, what is it?

Here it is:

```
>>> import math
>>> math.nextafter(0, 1)
5e-324
```

That's \(5 \times 10^{-324}\), it's freakishly small!

(Your result may differ from mine, although I'm not sure if it will. Leave a comment below if it does!)

So, what's the role of the `math.nextafter`

method?

```
>>> help(math.nextafter)
Help on built-in function nextafter in module math:
nextafter(x, y, /)
Return the next floating-point value after x towards y.
```

Hence, `nextafter`

looks at `x`

and then checks what's the float that's immediately next to `x`

,
if you walk in the direction of `y`

.
If I set `x`

to zero and `y`

to one, I get the smallest float that Python can represent on my machine.

So, what's the next float that Python can represent after `1`

?

Give it some thought.

Here it is:

```
>>> math.nextafter(1, 999)
1.0000000000000002
```

I'll be honest, for a second I thought it should've been `1 + 5e-324`

,
but it makes sense it wasn't that.
Floating point numbers have limited precision, right?
And one thing that's limited is the size of the mantissa:
the stuff that comes after the decimal point.

Above, we can see that the mantissa has 16 digits, and that's the size of the mantissa in Python.

So, what's the next number after `10`

?

Give it some thought.

Here it is:

```
>>> math.nextafter(10, 999)
10.000000000000002
```

If you count, now there's only 15 digits to the right of the decimal point... But...

]]>Este Pydon't mostra-te como usar as expressões condicionais de Python, a alternativa ao operador ternário em Python.

(Se és novo aqui e não sabes o que é uma Pydon't, então talvez queiras começar por ler a Proclamação das Pydon'ts.)

Infelizmente, para poupar algum tempo, ainda não traduzi este artigo para português... Hei de o fazer eventualmente... Se quiseres, deixa um comentário em baixo a pedir que eu traduza o artigo ASAP ou submete um PR com a tua tradução.

]]>Today I learned that files can *also* be unpacked in Python.

After learning that strings can be unpacked in Python, I shared the short article on Twitter.

As a reply, @dabeaz suggested I tried doing it with a file:

Now, try it with a file...

— David Beazley (@dabeaz) September 24, 2021

header, *data = open("somedata.csv")

After seeing how strings can be unpacked, unpacking a file didn't look so weird, but it was still a pleasant surprise!

But it “does make sense”, after all you can iterate directly over a file, which essentially iterates over the lines of the file.

Grab a CSV file `"my_data.csv"`

, for example with this data:

```
Name, Surnames
John, Doe
Mary, Smith
```

Then, in your Python REPL, you can get this to work:

```
>>> header, *data = open("my_data.csv")
>>> header
'Name, Surnames\n'
>>> data
['John, Doe\n', 'Mary, Smith\n']
```

It is not as useful as using the [`csv`

][csv] module to read the CSV
data in and process it, but it is still a nifty trick.

Come to think of it, if there is a place when this will be useful, probably won't be with CSV files...

I'll let you know if I put this little trick to good use!

That's it for now! Stay tuned and I'll see you around!

]]>Este Pydon't ensina-te a usar list comprehensions em Python.

(Se és novo aqui e não sabes o que é uma Pydon't, então talvez queiras começar por ler a Proclamação das Pydon'ts.)

Infelizmente, para poupar algum tempo, ainda não traduzi este artigo para português... Hei de o fazer eventualmente... Se quiseres, deixa um comentário em baixo a pedir que eu traduza o artigo ASAP ou submete um PR com a tua tradução.

]]>