skip to navigation
skip to content

Planet Python

Last update: December 02, 2022 04:41 PM UTC

December 02, 2022


Python for Beginners

Rename Index in a Pandas Series

We use pandas series objects for various data processing tasks in python. In this article, we will discuss how to rename the index in a pandas series.

Rename Index in a Pandas Series Using the index Attribute

When a series is created, the name of the index is empty. To rename the index of the series, you can use the name attribute of the series index object. You can assign the new index name to the name attribute of the index object to rename the series index as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters,index=numbers)
series.index.name="Numbers"
print("The series is:")
print(series)

Output:

The series is:
Numbers
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object

In the above example, We have first created a pandas series using the Series() constructor. Then, we assigned the string "Numbers" to the index.name attribute of the pandas series. Hence, the series index is renamed to "Numbers".

To rename the index of a series, you can also use the rename_axis() method. 

Rename the Index of a Series Using the rename_axis() Method

The rename_axis() method has the following syntax.

Series.rename_axis(mapper=_NoDefault.no_default, *, inplace=False, **kwargs)

Here, 

After execution, the rename_axis() method returns a new series with renamed index as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters,index=numbers)
series=series.rename_axis("Numbers")
print("The series is:")
print(series)

Output:

The series is:
Numbers
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object

In the above example, we first created a series. Then, we used the rename_axis() method to rename the index column of the series. Here, the rename_axis() method returns a new series instead of modifying the original series.

Suggested Reading: If you are into machine learning, you can read this MLFlow tutorial with code examples. You might also like this article on clustering mixed data types in Python.

Rename Index in a Series Inplace in Python

You can also modify the original series instead of creating a new series object after renaming the index. For this, you can set the inplace parameter to True in the rename_axis() method as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters,index=numbers)
series.rename_axis("Numbers",inplace=True)
print("The series is:")
print(series)

Output:

The series is:
Numbers
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object

In this example, we have set the inplace parameter to True in the rename_axis() parameter. Hence, the index of the original series has been renamed instead of creating a new series.

Conclusion

In this article, we have discussed how to rename the index in a pandas series using the index attribute and the renam_axis() method. To know more about the pandas module, you can read this article on how to sort a pandas dataframe. You might also like this article on how to drop columns from a pandas dataframe.

The post Rename Index in a Pandas Series appeared first on PythonForBeginners.com.

December 02, 2022 02:00 PM UTC


Real Python

The Real Python Podcast – Episode #135: Preparing Data to Measure True Machine Learning Model Performance

How do you prepare a dataset for machine learning (ML)? How do you go beyond cleaning the data and move toward measuring how the model performs? This week on the show, Jodie Burchell, developer advocate for data science at JetBrains, returns to talk about strategies for better ML model performance.


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

December 02, 2022 12:00 PM UTC


Lucas Cimon

Animated one-page-dungeon : Escape of the Torment

Last week, while translating John Harper's micro-TTRPG World of Dungeons: Turbo Breakers, I discovered the wonderful world of one page dungeons, starting with Michael Prescott splendid production at trilemma.com and also the yearly One Page Dungeon Context.

While crawling through the OPDC 2021 entries, I discovered a great map …


Permalink

December 02, 2022 11:56 AM UTC


Andrea Grandi

Ignoring hosts with python vcr when writing tests with pytest and generating cassettes

How to ignore hosts with python vcr when writing tests with pytest and generating cassettes

December 02, 2022 10:30 AM UTC


John Ludhi/nbshare.io

Understand Tensors With Numpy

December 02, 2022 07:39 AM UTC

December 01, 2022


PyCharm

PyCharm 2022.3 Is Out!

PyCharm 2022.3 brings an improved experience for working with asynchronous code in the Python Console – to call coroutines you can now use the await keyword outside of functions. The Python Packages tool window can now help you find, install, and delete packages from the Anaconda package library (in addition to PyPI). For pandas DataFrames there are new options to hide columns, use pagination to quickly go through rows, and export DataFrames in various formats.

Download PyCharm 2022.3

Conda in the Python Packages tool window

You can now search, install, and delete Conda packages through the Python Packages tool window, just one click away from the editor.

New Settings Sync solution

The new Settings Sync plugin is capable of syncing most of the shareable settings from the platform, bundled plugins, and some third-party plugins.

Enhanced UX for pandas DataFrames [Professional Edition]

Customize the way you work with DataFrames! You can now hide columns, use pagination to look through rows, export DataFrames in various formats, and more!

Improvements to docstrings rendering in Quick Documentation

The Quick Documentation popup now helps you quickly see the class attributes, as it now displays the Attributes section of the class docstrings. This also works for inherited class attributes and attributes of data classes.

asyncio support for the Python Console

The built-in Python Console now supports using the await keyword outside the function to quickly run a coroutine. PyCharm 2022.3 also adds asyncio support for the debugger. While useful for debugging asynchronous code, this feature is experimental and might not be fully stable. To enable it, follow the steps described here.

Frontend Development [Professional Edition]

Vitest support

PyCharm now supports Vitest, a Vite-native unit test framework! You can run, re-run, and debug your tests in all of the key ways you’d expect, including through gutter icons. Also, watch mode is enabled with the All Tests scenario by default. Snapshot testing and coverage are supported in watch mode, too, giving you near-instant feedback on coverage when coding.

New project templates for Next.js and Vite

The New Project wizard available on PyCharm’s Welcome screen now includes project templates for Vite and Next.js. We’ve also updated the project template for Vue to make sure it follows the latest standards.

Vue updates

PyCharm can now take care of unresolved imports and will offer suggestions for importing Vue components. We’ve also supported the props destructure syntax, improved the behavior of code completion and type checking for Vue library component props, and fixed several Nuxt 3 issues.

Redis support [Professional Edition]

With new Redis support, you can connect to Redis Single Instances, explore key values in the data viewer, write and execute Redis queries with the help of our smart coding assistance, and more.

Learn more

These are the most notable changes brought by the PyCharm 2022.3 release. You can find a more detailed list of updates on the What’s New page and in the release notes.

We’re always keen to receive your feedback about the new features and updates! Please share your thoughts and suggestions on Twitter, via our issue tracker, or in the comments section below.

December 01, 2022 08:04 PM UTC


Python Circle

Using git hooks to boost your productivity

Writing a proper git message is a good coding practice. A good commit message should clearly convey what has been changed and why it has been changed. Sometimes developers use an improper message in a hurry to commit and push the changes. Here is a working hook that enforces the developer to use a commit message of 30 characters. you may start including the branch name in the commit message using git hook.

December 01, 2022 04:39 PM UTC


Zero to Mastery

Python Monthly Newsletter 💻🐍

36th issue of the Python Monthly Newsletter! Read by 25,000+ Python developers every month. This monthly Python newsletter covers the latest Python news so that you stay up-to-date with the industry and keep your skills sharp.

December 01, 2022 10:00 AM UTC


Tryton News

Newsletter December 2022

After the release of series 6.6, support for series 6.2 has now ended and we are already back at work preparing and adding improvements such as these that have already landed:

Changes for the User

We now display the general ledger information like debit, credit and balance on the account form. This is useful when you are browsing account moves and you want to quickly check that information.

Changes for the Developer

The RPC calls to retrieve the list of selections for a field are now cached by the client for 1 day if they are based on a class method.

We’ve added an identifier_get method to the Product model that retrieves the first identifier that matches any of the identifier types passed.

The tariff codes now use the standard ir.calendar.month instead of a custom selection.

1 post - 1 participant

Read full topic

December 01, 2022 07:00 AM UTC


STX Next

What Is Django and What Is Django Used for?

If you love building software with Python and are looking to enter the field of web development, Django might be the tool that will help you realize your goals. One of the most popular Python frameworks, Django features excellent built-in solutions for almost every common task in web development. This means you can write code faster and deliver projects more efficiently.

December 01, 2022 01:12 AM UTC

10 Best Python Books

Python is an incredibly powerful programming language. Due to its versatile nature and simplicity, it continues to capture the interest of developers, those new and experienced alike.

December 01, 2022 12:33 AM UTC


Sebastian Witowski

dict() vs. {}

There are two different ways to create a dictionary. You can call the dict() function or use the literal syntax: {}. And in many cases, these are equivalent choices, so you might give it little thought and assume they both take the same amount of time.

But they don't!

Starting with this article, in my benchmarks, I have switched from Python 3.8 to 3.11. So if you're following the Writing Faster Python series and you're wondering why my code examples suddenly got a bit faster - that's the reason.

Check out the Upgrade Your Python Version article for a comparison of how much faster we can get by simply upgrading the CPython version.

# Python 3.11.0
$ python -m timeit "dict()"
10000000 loops, best of 5: 29.8 nsec per loop

$ python -m timeit "{}"
20000000 loops, best of 5: 14.2 nsec per loop

Benchmarking both versions shows that calling {} is twice as fast as calling dict(). And that's for Python 3.11. If you run the same examples with an older version of Python, dict() is even slower:

# Python 3.8.13
$ python -m timeit "dict()"
5000000 loops, best of 5: 57.2 nsec per loop

$ python -m timeit "{}"
20000000 loops, best of 5: 14.2 nsec per loop

Here dict() is almost four times as slow as {}.

Looking under the hood with the dis module #

Let's use the disassembler module to compare what's happening when we call dict() and {}:

>>> from dis import dis
>>> dis("dict()")
0 0 RESUME 0

1 2 PUSH_NULL
4 LOAD_NAME 0 (dict)
6 PRECALL 0
10 CALL 0
20 RETURN_VALUE
>>> dis("{}")
0 0 RESUME 0

1 2 BUILD_MAP 0
4 RETURN_VALUE

The dis module returns the bytecode instructions from a code snippet. It's an excellent way to see what's happening under the hood of your programs. Don't worry if all those cryptic names seem unfamiliar (if you're curious, check out the Python Bytecode Instructions). For us, the important instructions are BUILD_MAP and CALL.

When we call {}, we execute a Python statement, so Python immediately knows what to do - build a dictionary. In comparison, when we call dict(), Python has to find the dict() function and call it. That's because nothing stops you from overriding the dict() function. You can make it do something completely different than creating a dictionary, for example:

def dict(*args, **kwargs):
# Happy debugging ;)
return list([1, 2, 3])

Python doesn't stop you from overriding the built-in functions. So when you call dict(), the interpreter has to find this function and call it.

Is there any other difference? #

I tried to think of any other reason why you might use dict() over {}, and the only one that came to my mind was for creating a dictionary from an iterator.

Take a look at this example:

>>> iter = zip(['a', 'b', 'c'], [1,2,3])
>>> {iter}
{<zip at 0x102d57b40>} # This is not really what we want
>>> dict(iter)
{'a': 1, 'b': 2, 'c': 3} # Much better

We can't use the literal syntax to create a dictionary. We would have to use a dictionary comprehension: {k: v for k, v in iter}. But a simple dict(iter) looks much cleaner. Apart from this use case, I think it's mostly up to your preference which version you use.

There are also some interesting quirks that I found. For example, in CPython 3.6 and below, if you wanted to pass more than 255 arguments to a function, you would get a SyntaxError. So, in this case, dict() is a no-go, but {} should work. However, if you're passing over 255 parameters to a function, you probably have bigger problems in your code than wondering if the literal syntax is a few nanoseconds faster.

[] vs. list(), () vs. tuple, {'x', } vs. set(['x']) #

The same rule applies to using [] vs. list(), () vs. tuple(), or {'x',} vs. set(['x']). Using the literal syntax is faster than calling the corresponding function:

$ python -m timeit "list()"
10000000 loops, best of 5: 28.5 nsec per loop

$ python -m timeit "[]"
20000000 loops, best of 5: 12.7 nsec per loop

$ python -m timeit "tuple()"
50000000 loops, best of 5: 9.93 nsec per loop

$ python -m timeit "()"
50000000 loops, best of 5: 4.45 nsec per loop

$ python -m timeit "set(['x'])"
5000000 loops, best of 5: 72.7 nsec per loop

$ python -m timeit "{'x',}"
10000000 loops, best of 5: 29.5 nsec per loop

Of course, if you construct a large data structure, the difference between the two versions becomes unnoticeable:

$ python -m timeit "list(range(1_000_000))"
20 loops, best of 5: 14 msec per loop

$ python -m timeit "[*range(1_000_000)]"
20 loops, best of 5: 14 msec per loop

December 01, 2022 12:00 AM UTC

November 30, 2022


The Python Coding Blog

Argh! What are args and kwargs in Python? [Intermediate Python Functions Series #4]

In the first three articles in this Series, you familiarised yourself with the key terms when dealing with functions. You also explored positional and keyword arguments and optional arguments with default values. In this article, you’ll look at different types of optional arguments. Rather unfortunately, these are often referred to by the obscure names args and kwargs in Python.

Overview Of The Intermediate Python Functions Series

Here’s an overview of the seven articles in this series:

  1. Introduction to the series: Do you know all your functions terminology well?
  2. Choosing whether to use positional or keyword arguments when calling a function
  3. Using optional arguments by including default values when defining a function
  4. [This article] Using any number of optional positional and keyword arguments: *args and **kwargs
  5. Using positional-only arguments and keyword-only arguments: the “rogue” forward slash / or asterisk * in function signatures
  6. Type hinting in functions
  7. Best practices when defining and using functions

Using Any Number of Optional Positional Arguments: *args

The topic of *args may seem weird and difficult. However, it’s neither that strange nor that hard. The name is a bit off-putting, but once you understand what’s going on, *args will make sense.

Let’s dive in by looking at this code:

def greet_people(number, *people):
    for person in people:
        print(f"Hello {person}! How are you doing today?\n" * number)

Did you spot the asterisk in front of the parameter name people? You’ll often see the parameter name *args used for this, such as:

def greet_person(number, *args):

However, what makes this “special” is not the name ‘args’ but the asterisk * in front of the parameter name. You can use any parameter name you want. In fact, it’s best practice to use parameter names that describe the data rather than obscure terms. This is why I chose people in this example.

Let’s go back to the function you defined earlier. Consider the following three function calls:

# 1.
greet_people(3, "James", "Stephen", "Kate")

# 2.
greet_people(2, "Bob")

# 3.
greet_people(5)

All three function calls are valid:

To understand how this is possible, let’s dig a bit deeper into what the parameter people is. Let’s print it out and also print out its type. I’ll comment out the rest of the function’s body for the time being. I’m showing the output from the three function calls as comments in the code snippet below:

def greet_people(number, *people):
    print(people)
    print(type(people))
    # for person in people:
    #     print(f"Hello {person}! How are you doing today?\n" * number)

# 1.
greet_people(3, "James", "Stephen", "Kate")
# OUTPUT:
# ('James', 'Stephen', 'Kate')
# <class 'tuple'>

# 2.
greet_people(2, "Bob")
# OUTPUT:
# ('Bob',)
# <class 'tuple'>

# 3.
greet_people(5)
# OUTPUT:
# ()
# <class 'tuple'>

The local variable people inside the function is a tuple. Its contents are all the arguments you used in the function calls from the second argument onward. The first argument when you call greet_people() is the positional argument assigned to the first parameter: number. All the remaining arguments are collected in the tuple named people.

In the first function call, the first argument is the integer 3. Then there are three more arguments: "James", "Stephen", and "Kate". Therefore, people is a tuple containing these three strings.

In the second function call, the required positional argument is 2. Then, there’s only one additional argument: "Bob". Therefore, the tuple people contains just one item.

In the final function call, there are no additional arguments. The only argument is the required one which is assigned to number. Therefore, people is an empty tuple. It’s empty, but it still exists!

Let’s go back to the function definition you wrote at the start of this section and look at the output from the three function calls:

def greet_people(number, *people):
    for person in people:
        print(f"Hello {person}! How are you doing today?\n" * number)

# 1.
greet_people(3, "James", "Stephen", "Kate")

# 2.
greet_people(2, "Bob")

# 3.
greet_people(5)

The output from this code is:

Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?

Hello Stephen! How are you doing today?
Hello Stephen! How are you doing today?
Hello Stephen! How are you doing today?

Hello Kate! How are you doing today?
Hello Kate! How are you doing today?
Hello Kate! How are you doing today?

Hello Bob! How are you doing today?
Hello Bob! How are you doing today?

Since people is a tuple, you can iterate through it using a for loop. This way, you can perform the same action for each of the optional arguments assigned to the tuple people.

The first function call prints three blocks of output (the ones with James, Stephen, and Kate.) The second function call outputs the lines with Bob in them. The final function call doesn’t print anything since the tuple people is empty.

Therefore, when you add an *args to your function definition, you’re allowing any number of optional positional arguments to be added to the function call. Note that I used the term ‘positional’ in the last sentence. These arguments are collected into the args variable using their position in the function call. All the arguments that come after the required positional arguments are optional positional arguments.

Some rules when using *args

Let’s make a small change to the function definition from earlier:

def greet_people(*people, number):
    for person in people:
        print(f"Hello {person}! How are you doing today?\n" * number)

You’ve swapped the position of the parameters number and *people compared to the previous example. Let’s try this function call:

greet_people("James", "Kate", 5)

This raises the following error:

Traceback (most recent call last):
  File "...", line 5, in <module>
    greet_people("James", "Kate", 5)
TypeError: greet_people() missing 1 required keyword-only argument: 'number'

Note that the error is not raised by the function definition but by the function call. There’s a hint as to what the problem is in the error message. Let’s summarise the problem, and then I’ll expand further. All parameters which follow the *args parameter in the function definition must be used with keyword arguments.

What? And Why?

The parameter *people tells the function that it can accept any number of arguments which will be assigned to the tuple people. Since this could be any number, there’s no way for the program to know when you wish to stop adding these optional arguments and move on to arguments that are assigned to the next parameters, in this case, number.

Let’s fix the function call, and then we’ll come back to this explanation:

def greet_people(*people, number):
    for person in people:
        print(f"Hello {person}! How are you doing today?\n" * number)

greet_people("James", "Kate", number=5)

This code now works and gives the following output:

Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?

Hello Kate! How are you doing today?
Hello Kate! How are you doing today?
Hello Kate! How are you doing today?
Hello Kate! How are you doing today?
Hello Kate! How are you doing today?

Since the last argument is a named (keyword) argument, it’s no longer ambiguous that the value 5 should be assigned to the parameter name number. The program can’t read your mind! Therefore, it needs to be told when the optional positional arguments end since you can have any number of them. Naming all subsequent arguments removes all ambiguity and fixes the problem.

*args summary

Before moving on, let’s summarise what we’ve learned about *args.

Using Any Number of Optional Keyword Arguments: **kwargs

When you hear about *args in Python, you’ll often hear them mentioned in the same breath as **kwargs. They always seem to come as a pair: “Args and Kwargs in Python!” So let’s see what **kwargs are with the following example:

def greet_people(**people):
    for person, number in people.items():
        print(f"Hello {person}! How are you doing today?\n" * number)

As with ‘args’, there’s nothing special about the name ‘kwargs’. What makes a kwargs a kwargs (!) is the double asterisk ** in front of the parameter name. You can use a more descriptive parameter name when defining a function with **kwargs.

Let’s use the following three function calls as examples in this section:

# 1.
greet_people(James=5, Mark=2, Ishaan=1)

# 2.
greet_people(Stephen=4)

# 3.
greet_people()

Let’s explore the variable people inside the function. As you did earlier, you’ll print out its contents and its type. The rest of the function body is commented, out and the output from the three function calls is shown as comments:

def greet_people(**people):
    print(people)
    print(type(people))
    # for person, number in people.items():
    #     print(f"Hello {person}! How are you doing today?\n" * number)

# 1.
greet_people(James=5, Mark=2, Ishaan=1)
# OUTPUT:
# {'James': 5, 'Mark': 2, 'Ishaan': 1}
# <class 'dict'>

# 2.
greet_people(Stephen=4)
# OUTPUT:
# {'Stephen': 4}
# <class 'dict'>

# 3.
greet_people()
# OUTPUT:
# {}
# <class 'dict'>

The variable people is a dictionary. You used keyword (named) arguments in the function calls, not positional ones. Notice how the keywords you used when naming the arguments are the same as the keys in the dictionary. The argument is the value associated with that key.

For example, in the function call greet_people(James=5, Mark=2, Ishaan=1), the keyword argument James=5 became an item in the dictionary with the string "James" as key and 5 as its value, and so on for the other named arguments. You can include as many keyword arguments as you wish when you call a function with **kwargs.

You may be wondering where the name ‘kwargs’ comes from. Possibly you guessed this already: KeyWord ARGumentS.

Here’s the original function definition and the three function calls again:

def greet_people(**people):
    for person, number in people.items():
        print(f"Hello {person}! How are you doing today?\n" * number)

# 1.
greet_people(James=5, Mark=2, Ishaan=1)

# 2.
greet_people(Stephen=4)

# 3.
greet_people()

This code gives the following output:

Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?
Hello James! How are you doing today?

Hello Mark! How are you doing today?
Hello Mark! How are you doing today?

Hello Ishaan! How are you doing today?

Hello Stephen! How are you doing today?
Hello Stephen! How are you doing today?
Hello Stephen! How are you doing today?
Hello Stephen! How are you doing today?

Since people is a dictionary, you can loop through it using the dictionary method items(). The first function call prints out three sets of greetings for James, Mark, and Ishaan. The number of times each greeting is printed depends on the argument used. The second call displays four greetings for Stephen. The final call doesn’t display anything since the dictionary is empty.

**kwargs summary

In summary:

Combining *args and **kwargs

You now know about *args. You also know about **kwargs. Let’s combine both args and kwargs in Python functions.

Let’s look at this code. You have two teams (represented using the dictionaries red_team and blue_team) and the function adds members to one of the teams. Each member starts off with some number of points:

red_team = {}
blue_team = {}

def add_team_members(team, **people):
    for person, points in people.items():
        team[person] = points

add_team_members(red_team, Stephen=10, Kate=8, Sharon=12)

print(f"{red_team = }")
print(f"{blue_team = }")

What do you think the output will be?

The function definition has two parameters. The second one has the double asterisk ** in front of it, which makes it a ‘kwargs’. This means you can pass any number of keyword arguments which will be assigned to a dictionary with the name people.

Now, let’s look at the function call. There is one positional argument, red_team. There are also three keyword arguments. Remember that you can have as many keyword arguments as you want after the first required positional argument.

In the function definition’s body, people is a dictionary. Therefore, you can loop through it using items(). The variables person and points will contain the key and the value of each dictionary item. In the first iteration of the for loop, person will contain the string "Stephen" and points will contain 10. In the second for loop iteration, person will contain "Kate" and points will be 8. And "Sharon" and 12 will be used in the third loop iteration.

Here’s the output of the code above:

red_team = {'Stephen': 10, 'Kate': 8, 'Sharon': 12}
blue_team = {}

Only red_team has changed. blue_team is still the same empty dictionary you initialised at the beginning. That’s because you passed red_team as the first argument in add_team_members().

Some rules when using *args and **kwargs

As you’ve seen in the previous articles in this series and earlier in this one, there are always some rules on how to order the different types of parameters and arguments. Let’s look at a few of these rules here.

Let’s start with this example:

red_team = {}
blue_team = {}

def add_team_members(**people, team):
    for person, points in people.items():
        team[person] = points

You’ll get an error when you run this code even without a function call:

  File "...", line 4
    def add_team_members(**people, team):
                                   ^^^^
SyntaxError: arguments cannot follow var-keyword argument

Note: the error message in Python versions before 3.11 is different. The error says that the variable keyword parameter – that’s the **kwargs – must come after the other parameters.

Let’s make a change to the function before you explore some other options. You can check whether the name of the team member is already in the team and only add it if it’s not already there:

red_team = {"Stephen": 4}
blue_team = {}

def add_team_members(team, **people):
    for person, points in people.items():
        if person not in team.keys():
            team[person] = points
        else:
            print(f"{person} is already in the team")

add_team_members(red_team, Stephen=10, Kate=8, Sharon=12)

print(f"{red_team = }")
print(f"{blue_team = }")

The output from this code is:

Stephen is already in the team
red_team = {'Stephen': 4, 'Kate': 8, 'Sharon': 12}
blue_team = {}

Notice how "Stephen" is already in the dictionary with a value of 4 so the function doesn’t update it. Now, you can add another team and modify the function so that you can add team members to more than one team at a time in a single function call. People can be in more than one team:

red_team = {}
blue_team = {}
green_team = {}

def add_team_members(*teams, **people):
    for person, points in people.items():
        for team in teams:
            if person not in team.keys():
                team[person] = points
            else:
                print(f"{person} is already in the team.")

add_team_members(red_team, blue_team, Stephen=10, Kate=8, Sharon=12)
add_team_members(red_team, green_team, Mary=3, Trevor=15)
add_team_members(blue_team, Ishaan=8)

print(f"{red_team = }")
print(f"{blue_team = }")
print(f"{green_team = }")

You’re using both *args and **kwargs in this function. When you call the function, you can first use any number of positional arguments (without a keyword) followed by any number of keyword arguments:

The output from this code is:

red_team = {'Stephen': 10, 'Kate': 8, 'Sharon': 12, 'Mary': 3, 'Trevor': 15}
blue_team = {'Stephen': 10, 'Kate': 8, 'Sharon': 12, 'Ishaan': 8}
green_team = {'Mary': 3, 'Trevor': 15}

You’ll note that Stephen, Kate, and Sharon are in both the red team and the blue team. Mary and Trevor are in the red and green teams. Ishaan is just in the blue team.

Let’s get back to talking about the rules of what you can and cannot do. You can change the function call from the one you used earlier:

red_team = {}
blue_team = {}
green_team = {}

def add_team_members(*teams, **people):
    for person, points in people.items():
        for team in teams:
            if person not in team.keys():
                team[person] = points
            else:
                print(f"{person} is already in the team.")

add_team_members(Stephen=10, Kate=8, Sharon=12, red_team, blue_team)

The output from this code is the following error:

  File "...", line 13
    add_team_members(Stephen=10, Kate=8, Sharon=12, red_team, blue_team)
                                                                       ^
SyntaxError: positional argument follows keyword argument

You cannot place keyword (named) arguments before positional arguments when you call the function. This makes sense since *teams is listed before **people in the function signature. So, can you swap these over when you define a function? Let’s find out:

red_team = {}
blue_team = {}
green_team = {}

def add_team_members(**people, *teams):
    for person, points in people.items():
        for team in teams:
            if person not in team.keys():
                team[person] = points
            else:
                print(f"{person} is already in the team.")

The answer is “No”:

  File "...", line 5
    def add_team_members(**people, *teams):
                                   ^
SyntaxError: arguments cannot follow var-keyword argument

This is an error with the function definition, not the function call (there is no function call in this code!) Therefore, you must include the *args before the **kwargs when you define a function.

Final Words

There are more combinations of “normal” positional arguments, “normal” keyword arguments, *args, and **kwargs you could try. But we’ll draw a line here in this article as the main objective was to give you a good idea of what these types of arguments are and how you can use them.

Now that you know about args and kwargs in Python functions, you can move on to yet another type of argument. In the next article, you’ll read about positional-only arguments and keyword-only arguments.

Next Article: <Link will be posted here when the next article in the series is posted>

Further Reading


Get the latest blog updates

No spam promise. You’ll get an email when a new blog post is published


The post Argh! What are args and kwargs in Python? [Intermediate Python Functions Series #4] appeared first on The Python Coding Book.

November 30, 2022 09:23 PM UTC


Everyday Superpowers

Refactor Python for more satisfaction

This is a blogified version of my 18-minute PyJamas talk, Refactor refactoring—How changing your views on refactoring can make your job more satisfying.

You can watch it here:


Read more...

November 30, 2022 07:38 PM UTC


Real Python

Advent of Code: Solving Your Puzzles With Python

Advent of Code is an online Advent calendar where you’ll find new programming puzzles offered each day from December 1 to 25. While you can solve the puzzles at any time, the excitement when new puzzles unlock is really something special. You can participate in Advent of Code in any programming language—including Python!

With the help of this tutorial, you’ll be ready to start solving puzzles and earning your first gold stars.

In this tutorial, you’ll learn:

  • What an online Advent calendar is
  • How solving puzzles can advance your programming skills
  • How you can participate in Advent of Code
  • How you can organize your code and tests when solving Advent of Code puzzles
  • How test-driven development can be used when solving puzzles

Advent of Code puzzles are designed to be approachable by anyone with an interest in problem-solving. You don’t need a heavy computer science background to participate. Instead, Advent of Code is a great arena for learning new skills and testing out new features of Python.

Source Code: Click here to download the free source code that shows you how to solve Advent of Code puzzles with Python.

Puzzling in Programming?

Working on puzzles may seem like a waste of your available programming time. After all, it seems like you’re not really producing anything useful and you’re not advancing your current projects forward.

However, there are several advantages to taking some time off to practice with programming puzzles:

  • Programming puzzles are usually better specified and more contained than your regular job tasks. They offer you the chance to practice logical thinking on problems that are less complex than the ones you typically need to handle in your day job.

  • You can often challenge yourself with several similar puzzles. This allows you to build procedural memory, much like muscle memory, and get experience with structuring certain kinds of code.

  • Puzzles are often designed with an eye towards a solution. They allow you to learn about and apply algorithms that are tried and tested and are an important part of any programmer’s toolbox.

  • For some puzzle solutions, even the greatest supercomputers can be too slow if the algorithm is inefficient. You can analyze the performance of your solution and get experience to help you understand when a straightforward method is fast enough and when a more optimized procedure is necessary.

  • Most programming languages are well-suited for solving programming puzzles. This gives you a great opportunity to compare different programming languages for different tasks. Puzzles are also a great way of getting to know a new programming language or trying out some of the newest features of your favorite language.

On top of all of this, challenging yourself with a programming puzzle is often pretty fun! When you add it all up, setting aside some time for puzzles can be very rewarding.

Exploring Options for Solving Programming Puzzles Online

Luckily, there are many websites where you can find programming puzzles and try to solve them. There are often differences in the kinds of problems these websites present, how you submit your solutions, and what kind of feedback and community the sites can offer. You should therefore take some time to look around and find those that appeal the most to you.

In this tutorial, you’ll learn about Advent of Code, including what kind of puzzles you can find there and which tools and tricks you can employ to solve them. However, there are also other places where you can get started solving programming puzzles:

  • Exercism has learning tracks in many different programming languages. Each learning track offers coding challenges, small tutorials about different programming concepts, and mentors who give you feedback on your solutions.

  • Project Euler has been around for a long time. The site offers hundreds of puzzles, usually formulated as math problems. You can solve the problems in any programming language, and once you’ve solved a puzzle, you get access to a community thread where you can discuss your solution with others.

  • Code Wars offers tons of coding challenges, which they call katas. You can solve puzzles in many different programming languages with their built-in editor and automated tests. Afterward, you can compare your solutions to others’ and discuss strategies in the forums.

  • HackerRank has great features if you’re looking for a job. They offer certifications in many different skills, including problem-solving and Python programming, as well as a job board that lets you show off your puzzle-solving skills as part of your job applications.

There are many other sites available where you can practice your puzzle-solving skills. In the rest of this tutorial, you’ll focus on what Advent of Code has to offer.

Preparing for Advent of Code: 25 Fresh Puzzles for Christmas

It’s time for Advent of Code! It was started by Eric Wastl in 2015. Since then, a new Advent calendar of twenty-five new programming puzzles has been published every December. The puzzles have gotten more and more popular over the years. More than 235,000 people have solved at least one of the puzzles from 2021.

Note: Traditionally, an Advent calendar is a calendar used to count the days of Advent while waiting for Christmas. Over the years, Advent calendars have become more commercial and have lost some of their Christian connection.

Most Advent calendars start on December 1 and end on December 24, Christmas Eve, or December 25, Christmas Day. Nowadays, there are all kinds of Advent calendars available, including LEGO calendars, tea calendars, and cosmetics calendars.

In traditional Advent calendars, you open one door every day to reveal what’s inside. Advent of Code mimics this by giving you access to one new puzzle each day from December 1 to December 25. For each puzzle you solve, you’ll earn gold stars that are yours to keep.

In this section, you’ll get more familiar with Advent of Code and see a glimpse of your first puzzle. Later, you’ll look at the details of how you can solve these puzzles and practice solving a few of the puzzles yourself.

Advent of Code Puzzles

Advent of Code is an online Advent calendar where a new puzzle is published every day from December 1 to December 25. Each puzzle becomes available at midnight, US Eastern Time. An Advent of Code puzzle has a few typical characteristics:

  • Each puzzle consists of two parts, but the second part isn’t revealed until you finish the first part.
  • You’ll earn one gold star (⭐) for each part that you finish. This means you can earn two stars per day and fifty stars if you solve all the puzzles for one year.
  • The puzzle is the same for everyone, but you need to solve it based on personalized input that you get from the Advent of Code site. This means that your answer to a puzzle will be different from someone else’s, even if you use the same code to calculate it.

You can participate in a global race to be the first to solve each puzzle. However, this is usually pretty crowded with highly skilled, competitive programmers. Advent of Code is probably going to be more fun if you use it as practice for yourself or if you challenge your friends and coworkers to a small, friendly competition.

Read the full article at https://realpython.com/python-advent-of-code/ »


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

November 30, 2022 02:00 PM UTC


Python for Beginners

Drop Elements From a Series in Python

Pandas series is very useful for handling data having ordered key-value pairs. In this article, we will discuss different ways to drop elements from a pandas series.

Drop Elements From a Pandas Series Using the drop() Method

We can drop elements from a pandas series using the drop() method. It has the following syntax.

Series.drop(labels=None, *, axis=0, index=None, columns=None, level=None, inplace=False, errors='raise')

Here, 

After execution, the drop() method returns a new series if the inplace parameter is set to False. Otherwise, it returns None. 

Drop a Single Element From a Pandas Series

To drop a single element from a series, you can pass the index of the element to the labels parameter in the drop() method as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series=series.drop(labels=11)
print("The modified series is:")
print(series)

Output:

The original series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object
The modified series is:
3        a
23       b
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object

In the above example, we first created a Series object using the Series() constructor. Then we dropped the element having index 11 using the drop() method. For this, we have passed the value 11 to the drop() method. After execution of the drop() method, you can observe that the element with index 11 has been removed from the output series.

Instead of the labels parameter, you can also use the index parameter in the drop() method as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series=series.drop(index=11)
print("The modified series is:")
print(series)

Output:

The original series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object
The modified series is:
3        a
23       b
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object

In this example, we have used the index parameter instead of the labels parameter. However, the resultant series after execution of the drop() method is the same in both cases.

Delete Multiple Elements From a Pandas Series

To drop multiple elements from a series, you can pass a python list of indices of the elements to be deleted to the labels parameter. For instance, if you want to delete elements at indices 11, 16, and 2 of the given Series, you can pass the list [11,16,2] to the labels parameter in the drop() method as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series=series.drop(labels=[11,16,2])
print("The modified series is:")
print(series)

Output:

The original series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object
The modified series is:
3      a
23     b
14    ab
45    bc
65     d
dtype: object

In this example, we have passed the list [11, 16, 2] as input to the labels parameter. Hence, after execution of the drop() method, the elements at index 11, 16, and 2 are deleted from the original series object.

Instead of the labels parameter, you can pass the list of indices to the index parameter as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series=series.drop(index=[11,16,2])
print("The modified series is:")
print(series)

Output:

The original series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object
The modified series is:
3      a
23     b
14    ab
45    bc
65     d
dtype: object

Drop Elements Inplace From a Pandas Series

By default, the drop() method returns a new series and doesn’t delete specified elements from the original series. To drop elements inplace from a pandas series, you can set the inplace parameter to True as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series.drop(index=[11,16,2],inplace=True)
print("The modified series is:")
print(series)

Output:

The original series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object
The modified series is:
3      a
23     b
14    ab
45    bc
65     d
dtype: object

In all the previous examples, the drop() method returned a new Series object. In this example, we have set the inplace parameter to True in the drop() method. Hence, the elements are deleted from the original series and it is modified. In this case, the drop() method returns None.

Delete an Element From a Series if the Index Exists

While deleting elements from a series using the drop() method, it is possible that we might pass an index to the labels or index parameter that is not present in the Series object. If the value passed to the labels or index parameter isn’t present in the Series, the drop() method runs into a KeyError exception as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series.drop(index=1117,inplace=True)
print("The modified series is:")
print(series)

Output:

KeyError: '[1117] not found in axis'

In the above example, we have passed the value 1117 to the index parameter. As the value 1117 is not present in the Series, we get a KeyError exception.

To avoid errors and drop elements from a series if the index exists, you can use the errors parameter. By default, the errors parameter is set to "raise". Due to this, the drop() method raises an exception every time it runs into an error. To suppress the exception, you can set the errors parameter to “ignore” as shown in the following example.

import pandas as pd
import numpy as np
letters=["a","b","c","ab","abc","abcd","bc","d"]
numbers=[3,23,11,14,16,2,45,65]
series=pd.Series(letters)
series.index=numbers
print("The original series is:")
print(series)
series.drop(index=1117,inplace=True,errors="ignore")
print("The modified series is:")
print(series)

Output:

The original series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object
The modified series is:
3        a
23       b
11       c
14      ab
16     abc
2     abcd
45      bc
65       d
dtype: object

In the above example, we have passed the value 1117 to the index parameter. As 1117 is not present in the series index, the drop() method would have run into a KeyError exception. However, we have set the errors parameter to "ignore" in the drop() method. Hence, it suppresses the error. You can also observe that the series returned by the drop() method is the same as the original series.

Suggested Reading: If you are into machine learning, you can read this article on regression in machine learning. You might also like this article on clustering mixed data types in Python.

Drop NaN Values From a Pandas Series

NaN values are special numbers having floating-point data type in Python. NaN values are used to represent the absence of a value. Most of the times, NaN values have no importance in a given dataset and we need to remove these values.

You can drop NaN values from a pandas series using the dropna() method. It has the following syntax.

Series.dropna(*, axis=0, inplace=False, how=None)

Here,  

After execution, the dropna() method returns a new series if the inplace parameter is set to False. Otherwise, it returns None. 

You can drop nan values from a pandas series as shown in the following example.

import pandas as pd
import numpy as np
letters=["a","b","c",np.nan,"ab","abc",np.nan,"abcd","bc","d"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series=series.dropna()
print("The modified series is:")
print(series)

Output:

The original series is:
0       a
1       b
2       c
3     NaN
4      ab
5     abc
6     NaN
7    abcd
8      bc
9       d
dtype: object
The modified series is:
0       a
1       b
2       c
4      ab
5     abc
7    abcd
8      bc
9       d

In the above example, you can observe that the original series has two NaN values. After execution, the dropna() method deletes both the NaN values with their indices and returns a new series.

Drop NaN Values Inplace From a Pandas Series

If you want to drop NaN values from the original series instead of creating a new series, you can set the inplace parameter to True in the dropna() method as shown below.

import pandas as pd
import numpy as np
letters=["a","b","c",np.nan,"ab","abc",np.nan,"abcd","bc","d"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series.dropna(inplace=True)
print("The modified series is:")
print(series)

Output:

import pandas as pd
import numpy as np
letters=["a","b","c",np.nan,"ab","abc",np.nan,"abcd","bc","d"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series.dropna(inplace=True)
print("The modified series is:")
print(series)

Here, we have set the inplace parameter to True. Hence, the dropna() method modified the original series instead of creating a new one. In this case, the dropna() method returns None after execution.

Drop Duplicates From a Pandas Series

We data preprocessing, we often need to remove duplicate values from the given data. To drop duplicate values from a pandas series, you can use the drop_duplicates() method. It has the following syntax.

Series.drop_duplicates(*, keep='first', inplace=False)

Here,

After execution, the drop_duplicates() method returns a new series if the inplace parameter is set to False. Otherwise, it returns None. You can observe this in the following example.

import pandas as pd
import numpy as np
letters=["a","b","a","a","ab","abc","ab","abcd","bc","abc","ab"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series=series.drop_duplicates()
print("The modified series is:")
print(series)

Output:

The original series is:
0        a
1        b
2        a
3        a
4       ab
5      abc
6       ab
7     abcd
8       bc
9      abc
10      ab
dtype: object
The modified series is:
0       a
1       b
4      ab
5     abc
7    abcd
8      bc
dtype: object

In the above example, you can observe that strings “a”, “ab”, and “abc” are present multiple times in the series. Hence, when we invoke the drop_duplicates() method on the series objects, all the duplicates except the one occurrence of the strings are removed from the series.

Looking at the indices, you can observe that first occurrence of the elements have been retained if the elements are present multiple times in the series. If you want to preserve the last occurrence of the elements having duplicate values, you can set the keep parameter to "last" as shown below.

import pandas as pd
import numpy as np
letters=["a","b","a","a","ab","abc","ab","abcd","bc","abc","ab"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series=series.drop_duplicates(keep="last")
print("The modified series is:")
print(series)

Output:

The original series is:
0        a
1        b
2        a
3        a
4       ab
5      abc
6       ab
7     abcd
8       bc
9      abc
10      ab
dtype: object
The modified series is:
1        b
3        a
7     abcd
8       bc
9      abc
10      ab
dtype: object

In the above example, we have set the keep parameter to "last". Hence, you can observe that the drop_duplicates() method preserves the last occurrence of the elements that have duplicate values.

Drop Duplicates Inplace in a Pandas Series

By default, the drop_duplicates() method doesn’t modify the original series object. It returns a new series. If you want to modify the original series by deleting the duplicates, you can set the inplace parameter to True in the drop_duplicates() method as shown below.

import pandas as pd
import numpy as np
letters=["a","b","a","a","ab","abc","ab","abcd","bc","abc","ab"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series.drop_duplicates(inplace=True)
print("The modified series is:")
print(series)

Output:

The original series is:
0        a
1        b
2        a
3        a
4       ab
5      abc
6       ab
7     abcd
8       bc
9      abc
10      ab
dtype: object
The modified series is:
0       a
1       b
4      ab
5     abc
7    abcd
8      bc
dtype: object

In this example, we have set the inplace parameter to True. Hence, the drop_duplicates() method modified the original series instead of creating a new one. In this case, the drop_duplicates() method returns None after execution.

Drop All Duplicate Values From a Pandas Series

To drop all the duplicates from a pandas series, you can set the keep parameter to False as shown below.

import pandas as pd
import numpy as np
letters=["a","b","a","a","ab","abc","ab","abcd","bc","abc","ab"]
series=pd.Series(letters)
print("The original series is:")
print(series)
series=series.drop_duplicates(keep=False)
print("The modified series is:")
print(series)

Output:

The original series is:
0        a
1        b
2        a
3        a
4       ab
5      abc
6       ab
7     abcd
8       bc
9      abc
10      ab
dtype: object
The modified series is:
1       b
7    abcd
8      bc
dtype: object

In this example, we have set the keep parameter to False in the drop_duplicates() method. Hence, you can observe that all the elements having duplicate values are removed from the series.

Conclusion

In this article, we have discussed different ways to drop elements from a pandas series. To know more about pandas module, you can read this article on how to sort a pandas dataframe. You might also like this article on how to drop columns from a pandas dataframe.

The post Drop Elements From a Series in Python appeared first on PythonForBeginners.com.

November 30, 2022 02:00 PM UTC


John Ludhi/nbshare.io

Pandas Read and Write Excel File

November 30, 2022 07:38 AM UTC


Spyder IDE

Improvements to the Spyder IDE installation experience

Juan Sebastian Bautista, C.A.M. Gerlach and Carlos Cordoba also contributed to this post.

Spyder 5.4.0 was released recently, featuring some major enhancements to its Windows and macOS standalone installers. You'll now get more detailed feedback when new versions are available, and you can download and start the update to them from right within Spyder, instead of having to install them manually. In this post, we'll go over how these new update features work and how you can start using them!

Before proceeding, we want to acknowledge that this work was made possible by a Small Development Grant awarded to Spyder by NumFOCUS, which has enabled us to hire a new developer (Juan Sebastian Bautista Rojas) to be in charge of all the implementation details.

Before these improvements, Spyder already had a mechanism to detect more recent versions, but that functionality was very simple. There was a pop-up dialog warning that a new version was available, but users had to follow a link to manually download the installer and then run it themselves:

Update available dialog from Spyder version 5.3.3 to 5.4.0 - Old update available dialog

Once you upgrade to Spyder 5.4.0 or above, you'll get this message on future Spyder updates:

Update available dialog from Spyder version 5.4.0 to 5.4.1 - New update available dialog

Spyder will now be able to automatically download and install a new version for you, much like many other popular applications.

After clicking "Yes" on that dialog, Spyder will display another with the status and percent completion of the download.

Dialog downloading Spyder 5.4.1 installer

If it is closed, the download will continue in the background, with its progress shown in a new status bar widget.

User clicking the update status in the taskbar to monitor the download status

After the download completes, Spyder will ask if you want to update immediately, cancel the update or defer it to when you close Spyder, to avoid interrupting your current workflow.

Options available to run installer, run installer after Spyder closes or not run it at all

If you chose to update immediately, or once you close Spyder if you deferred the update, our installer will be started automatically. On Windows, the installer has a series of automated prompts to close the current instance, uninstall the previous version and finally install the new one:

Series of screenshots of intallation steps, with arrows between: Yes/no dialog to close spyder, confirm close dialog, uninstall old version dialog, uninstalling status and finally installation wizard

On macOS, Spyder will automatically mount the new version's DMG, so you can simply drag and drop it in the Applications folder

macOS new version's DMG mounted with a drag and drop dialog to move new Spyder version into the Application folder

We hope these improvements will make updating to future Spyder versions smoother and more straightforward, so we can bring you new features and enhancements more easily in the future!

November 30, 2022 12:00 AM UTC

November 29, 2022


PyCoder’s Weekly

Issue #553 (Nov. 29, 2022)

#553 – NOVEMBER 29, 2022
View in Browser »

The PyCoder’s Weekly Logo


Microsoft Power BI and Python: Two Superpowers Combined

In this tutorial, you’ll learn how to install and configure Microsoft Power BI to work with Python. Using Python, you’ll import data from a SQLite database, transform and augment your dataset with pandas, and visualize it with Matplotlib.
REAL PYTHON

Parallel Nested for-Loops in Python

Nested for-loops often are an opportunity for parallel code. This article covers when it is a good idea to split them up and the variety of different parallel coding approaches you can use.
JASON BROWNLEE

Painless and Worry Free Postgres

alt

Find out why vanilla Postgres is a more cost effective and simple way to manage your database than shiny Postgres “compatible” databases. Performance tuned, no-forks, quality Postgres with expert support. Built for developer experience. Get started today →
CRUNCHY DATA sponsor

How We Run Tests in Hundreds of Environments Really Fast

Anton describes the test setup at Sentry and how they use both tox in parallel as well as GitHub actions to run a large test suite quickly.
ANTON PIRKER

Discussions

The Origins of Python

This discussion is around the excellent article by Lambert Meertens called The Origins of Python that delves into Python’s history.
HACKER NEWS

Python Jobs

Software Engineer - Weissman Lab (Cambridge, MA, USA)

Whitehead Institute for Biomedical Research

Senior Software Engineer (Python, Qt, Linux) (Anywhere)

VIOLET

More Python Jobs >>>

Articles & Tutorials

Everyday Project Packaging With pyproject.toml

In this Code Conversation video course, you’ll learn how to package your everyday projects with pyproject.toml. Playing on the same team as the import system means you can call your project from anywhere, ensure consistent imports, and have one file that’ll work for many build systems.
REAL PYTHON course

Always Use [closed, open) Intervals

“Intervals or ranges pop-up everywhere in the programming world. The classic example is picking a start and end date, like you would when booking an AirBnB or a flight. Have you ever wondered why they are always implemented as [closed, open) as opposed to [closed, closed]?”
FERNANDO HURTADO CARDENAS

Top 10 Vulns Impacting Open Source in 2022

alt

You might know all about the incredibly useful and insightful OWASP Top 10 list from 2021, but what about the exact CVEs that could be lurking in your applications? Check out Snyk Top 10 Open Source Vulnerability report to get up to date on 2022’s most common vulnerabilities →
SNYK.IO sponsor

16 Reasons to Use VS Code for Developing Jupyter Notebooks

“Visual Studio Code is one of the most popular text editors with a track record of continual improvements. One area where VS Code has been recently innovating is its Jupyter Notebook support.” Read on to see how this might help you.
CHRIS MOFFITT

Python Bytecode Explained

When a Python program is run, the interpreter first parses your code and checks for syntax errors, then it translates it into bytecode instructions. This article explains some of the features of Python bytecode.
MOSER MICHAEL

plydata: Piping for Pandas

The plydata Python package enables you to use the pipe operator, ">>", to chain operations on a pandas dataframe. Read on to learn how to use it and how it compares to the equivalent operation in R.
MARCIN KOZAK • Shared by Marcin

REPL Driven Development

REPL Driven Development is about fast feedback loops during development. It is not about typing code into a terminal window. David talks about this coding workflow and how it is similar to TDD.
DAVID VUJIC • Shared by David Vujic

Python JSONPath With Example

JSONPath is an expression language that is used to parse the JSON data in Python, similar to XPath in XML. This article covers the basics of finding paths in JSON using the library.
SRINIVAS RAMAKRISHNA

Deploy Django, Celery, Redis & Postgres With Docker-Compose

Deployments can be painful. This article describes one approach to deploying Django, Celery, Redis, and Postgres with docker-compose so you can reuse it in your app!
PIOTR PŁOŃSKI • Shared by Piotr Płoński

Deepnote Is a Modern Notebook Where Data Teams Go to Explore, Collaborate, and Solve Hard Problems

Explore data with Python & SQL from your browser. Add context with data visualizations and rich text editing. Share analysis with stakeholders by simply sending a link. Plans start at $0 (free).
DEEPNOTE sponsor

Private, “Protected” Attributes in Python Demystified

A guide to private and protected attributes in Python, learn all about when to use and when not to use leading underscores and double underscores (dunder).
AMIR AFIANIAN • Shared by Amir Afianian

Investigating a Backdoored PyPI Package Targeting FastAPI

Using an open source security scanner, the authors found a backdoored package on PyPI. Read on for details about how they found it and what it contained.
TURCKHEIM & TAFANI-DEREEPER

Projects & Code

quickadd: Parse Natural Language Time and Date Expressions

GITHUB.COM/ACREOM

Python Tools for the Polylith Architecture

GITHUB.COM/DAVIDVUJIC • Shared by David Vujic

django-virtual-models: Django ORM Prefetching Layer

GITHUB.COM/VINTASOFTWARE

Colossal-AI: Unified Deep Learning System for Big Model Era

GITHUB.COM/HPCAITECH

pytorch-image-models: Models, Scripts, Pre-Trained Weights

GITHUB.COM/RWIGHTMAN

Events

NZPUG-Auckland Coding Challenge “Office Hours”

November 30, 2022
MEETUP.COM

Deep Learning With PyTorch

November 30, 2022
MEETUP.COM

Weekly Real Python Office Hours Q&A (Virtual)

November 30, 2022
REALPYTHON.COM

PyDelhi User Group Meetup

December 3, 2022
MEETUP.COM

Sydney Python User Group (SyPy)

December 1, 2022
SYPY.ORG


Happy Pythoning!
This was PyCoder’s Weekly Issue #553.
View in Browser »

alt

[ Subscribe to 🐍 PyCoder’s Weekly 💌 – Get the best Python news, articles, and tutorials delivered to your inbox once a week >> Click here to learn more ]

November 29, 2022 07:30 PM UTC


PyCharm

The Second Release Candidate for PyCharm 2022.3 Is Out!

PyCharm 2022.3 is just around the corner and the second Release Candidate is here!

You can download the new version from our website, update directly from the IDE or via the free Toolbox App, or use snaps for Ubuntu.

Download PyCharm 2022.3 RC

This build requires an active subscription to PyCharm Professional.

Here are the most important improvements in this build:

Please share your feedback in the comments, on Twitter, or on our issue tracker. Your feedback is highly appreciated!

November 29, 2022 05:21 PM UTC


Real Python

Using Python's pathlib Module

Have you struggled with file path handling in Python? With the pathlib module, the struggle is now over! You no longer need to scratch your head over code like this:

>>>
>>> path.rsplit('\\', maxsplit=1)[0]

And you don’t have to cringe at the verbosity of something like this:

>>>
>>> os.path.isfile(os.path.join(os.path.expanduser('~'), 'realpython.txt'))

In this video course, you’ll learn how to:

Using the pathlib module, the two examples above can be rewritten using elegant, readable, and Pythonic code:

>>>
>>> path.parent
>>> (pathlib.Path.home() / 'realpython.txt').is_file()

That’s what you’ll master in this video course!


[ Improve Your Python With 🐍 Python Tricks 💌 – Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

November 29, 2022 02:00 PM UTC


Django Weblog

2023 DSF Board Election Results

The 2023 Board is transitional to our new staggered 2 year term board membership model.
Here are the results of this year's election as selected by ranked choice voting:

  1. Chaim Kirby*
  2. Jacob Kaplan-Moss
  3. Katie McLaughlin*
  4. Aaron Bassett
  5. Kátia Yoshime Nakamura*
  6. Peter Baumgartner
  7. Cagil Ulusahin Sonmez*
* Elected to a two (2) year term

Congratulations to our winners and a huge thank you to our departing board members Anna Makarudze, Mfon Eti-mfon, William Vincent, and Žan Anderle. You all set the bar very high for the next board.

Also a special thank you to the 30 candidates we had this year. The DSF simply isn't possible without the help of all of our volunteers.

November 29, 2022 01:19 PM UTC


S. Lott

Functional Programming and Finite State Automata (FSA)

When I talk about functional programming in Python, folks like to look for place where functional programming isn't appropriate. They latch onto finite-state automata (FSA) because "state" of an automata doesn't seem to fit with stateless objects used in functional programming.

This is a false dichotomy. 

It's emphatically false in Python, where we don't have a purely functional language.

(In a purely functional language, monads can help make FSA's behave properly and avoid optimization. The use of a recursion to consume an iterable and make state transitions is sometimes hard to visualize. We don't have these constraints.)

Let's look at a trivial kind of FSA: the parity computation. We want to know how many 1-bits are in a given value. Step 1 is to expand an integer into bits.

def bits(n: int) -> Iterable[int]:
if n < 0:
raise ValueError(f"{n} must be >= 0")
while n > 0:
n, bit = divmod(n, 2)
yield bit

This will transform a number into a sequence of bits. (They're in order from LSB to MSB, which is the reverse order of the bin() function.)

>>> list(bits(42))
[0, 1, 0, 1, 0, 1]

Given a sequence of bits, is there an odd number or an even number? This is the parity question. The parity FSA is often depicted like this:

When the parity is in the even state, a 1-bit transitions to the odd state. When the parity is in the odd, a 1-bit transitions to the even state.

Clearly, this demands the State design pattern, right?

An OO Implementation

Here's a detailed OO implementation using the State design pattern.

 
class Parity:
def signal(self, bit: int) -> "Parity":
...


class EvenParity(Parity):
def signal(self, bit: int) -> Parity:
if bit % 2 == 1:
return OddParity()
else:
return self


class OddParity(Parity):
def signal(self, bit: int) -> Parity:
if bit % 2 == 1:
return EvenParity()
else:
return self


class ParityCheck:
def __init__(self):
self.parity = EvenParity()

def check(self, message: Iterable[int]) -> None:
for bit in message:
self.parity = self.parity.signal(bit)

@property
def even_parity(self) -> bool:
return isinstance(self.parity, EvenParity)

Each of the Parity subclasses implements one of the states of the FSA. The lonely signal() method implements state-specific behavior. In this case, it's a transition to another state. In more complex examples it may involve side-effects like updating a mutable data structure to log progress.

This mapping from state to diagram to class is pretty pleasant. Folks really like to implement each state as a distinct class. It somehow feels really solid.

It's import to note the loneliness of the lonely signal() method. It's all by itself in that big, empty class.

Hint. This could be a function.

It's also important to note that this kind of design is subject to odd, unpleasant design tweaks. Ideally, the transition is *only* done by the lonely signal() method. Nothing stops the unscrupulous programmer from putting state transitions in other methods. Sigh.

We'll look at more complex kinds of state transitions later. In the UML state chart diagrams sates may also have entry actions and exit actions, a bit more complex behavior than we we're showing in this example.

A Functional Implementation

What's the alternative? Instead of modeling state as an object with methods for behavior, we can model state as a function. The state is a function that transitions to the next state.

def even(bit: int) -> ParityF:
if bit % 2 == 1:
return odd
else:
return even


def odd(bit: int) -> ParityF:
if bit % 2 == 1:
return even
else:
return odd


def parity_check(message: Iterable[int], init: ParityF = None) -> ParityF:
parity = init or even
for bit in message:
parity = parity(bit)
return parity


def even_parity(p: ParityF) -> bool:
return p is even

Each state is modeled by a function.

The parity_check() function examines each bit, and applies the current state function (either even() or odd()) to compute the next state, and save this as the vakue of the parity variable.

What's the ParityF type? This:

from typing import Protocol


class ParityF(Protocol):
def __call__(self, bit: int) -> "ParityF":
...

This uses a Protocol to define a type with a recursive cycle in it. It would be more fun to use something like ParityF = Callable[[int], "ParityF"], but that's not (yet) supported.

Some Extensions

What if we need each state to have more attributes?

Python functions have attributes. Like this: even.some_value = 2; odd.some_value = 1. We can add all the attributes we require.

What about other functions that happen on entry to a state or exit from a state? This is trickier. My preference is to use a class as a namespace that contains a number of related functions.

class Even:
@staticmethod
def __call__(bit: int) -> ParityF:
if bit % 2 == 1:
odd.enter()
return odd
else:
return even
@staticmethod
def enter() -> None:
print("even")

even = Even()

This seems to work out well, and keeps each state-specific material in a single namespace. It uses static methods to follow the same design principle as the previous example -- these are pure functions, collected into the class only to provide a namespace so we can use odd.enter() or even.enter().

TL;DR

The State design pattern isn't required to implement a FSA.

November 29, 2022 11:00 AM UTC


testmon

pytest-testmon v1.4 with xdist support is out.

I'm happy to announce that this long-standing limitation of pytest-testmon has been fixed.

To find more go to: New features in 1.4.x

November 29, 2022 08:49 AM UTC


Talk Python to Me

#391: Pyscript powered by MicroPython

No Python announcement of 2022 was met with more fanfare than pyscript. This project, announced at PyCon 2022, allows you to write Python files and run them in your browser in place of JavaScript or even with interactions between Python and JavaScript. There was just one catch: The runtime download was a 9MB WebAssembly file. That made its uses quite limited. <br/> <br/> On this episode, we dive into some news that might change that calculus. The MicroPython and PyScript folks have been teaming up to get PyScript running in the browser on MicroPython. Yes, that's the embedded chip Python. Here's the good news: MicroPython's WebAssembly is just 300k to download and loads in under 100ms. Now that could unlock some possibilities. <br/> <br/> We have Brett Cannon, Nicholas Tollervey, and Fabio Pliger on the show to discuss. <br/> <br/> <br/> YOUTUBE: id=ABVn6uMG1OI<br/> <br/> <strong>Links from the show</strong><br/> <br/> <div><b>Guests and Host Links</b><br/> <b>Brett Cannon</b>: <a href="https://fosstodon.org/@brettcannon" target="_blank" rel="noopener">@brettcannon@fosstodon.org</a><br/> <b>Nicholas Tollervey</b>: <a href="https://mastodon.social/@ntoll" target="_blank" rel="noopener">@ntoll@mastodon.social</a><br/> <b>Fabio Pliger</b>: <a href="https://twitter.com/b_smoke" target="_blank" rel="noopener">@b_smoke</a><br/> <b>Michael Kennedy</b>: <a href="https://fosstodon.org/@mkennedy" target="_blank" rel="noopener">@mkennedy@fosstodon.org</a><br/> <br/> <b>Web Assembly</b>: <a href="https://developer.mozilla.org/en-US/docs/WebAssembly" target="_blank" rel="noopener">developer.mozilla.org</a><br/> <b>pyodide</b>: <a href="https://pyodide.org/en/stable/" target="_blank" rel="noopener">pyodide.org</a><br/> <b>micropython</b>: <a href="https://www.micropython.org" target="_blank" rel="noopener">micropython.org</a><br/> <b>Picture of TFT ESP32 Board</b>: <a href="https://pythonbytes.fm/episodes/youtube_image/A8nwbD3loIs" target="_blank" rel="noopener">pythonbytes.fm</a><br/> <b>pyscript</b>: <a href="https://pyscript.net" target="_blank" rel="noopener">pyscript.net</a><br/> <b>Simon Willison's Post About micropython + pyscript</b>: <a href="https://fedi.simonwillison.net/@simon/109314695365668203" target="_blank" rel="noopener">fedi.simonwillison.net</a><br/> <b>WASI</b>: <a href="https://github.com/WebAssembly/WASI" target="_blank" rel="noopener">github.com</a><br/> <b>Watch this episode on YouTube</b>: <a href="https://www.youtube.com/watch?v=ABVn6uMG1OI" target="_blank" rel="noopener">youtube.com</a><br/> <b>Episode transcripts</b>: <a href="https://talkpython.fm/episodes/transcript/391/pyscript-powered-by-micropython" target="_blank" rel="noopener">talkpython.fm</a><br/> <br/> <b>--- Stay in touch with us ---</b><br/> <b>Subscribe to us on YouTube</b>: <a href="https://talkpython.fm/youtube" target="_blank" rel="noopener">youtube.com</a><br/> <b>Follow Talk Python on Mastodon</b>: <a href="https://fosstodon.org/web/@talkpython" target="_blank" rel="noopener"><i class="fa-brands fa-mastodon"></i>talkpython</a><br/> <b>Follow Michael on Mastodon</b>: <a href="https://fosstodon.org/web/@mkennedy" target="_blank" rel="noopener"><i class="fa-brands fa-mastodon"></i>mkennedy</a><br/></div><br/> <strong>Sponsors</strong><br/> <a href='https://talkpython.fm/awsinsiders'>AWS Insiders</a><br> <a href='https://talkpython.fm/max'>Local Maximum Podcast</a><br> <a href='https://talkpython.fm/assemblyai'>AssemblyAI</a><br> <a href='https://talkpython.fm/training'>Talk Python Training</a>

November 29, 2022 08:00 AM UTC