Tags

args-kwargs

These special parameters allow functions to take arbitrary amounts of positional and keyword arguments. The names args and kwargs are purely convention, and could be named any other valid variable name. The special functionality comes from the single and double asterisks (*). If both are used in a function signature, *args must appear before **kwargs.

Single asterisk *args will ingest an arbitrary amount of positional arguments, and store it in a tuple. If there are parameters after *args in the parameter list with no default value, they will become required keyword arguments by default.

Double asterisk **kwargs will ingest an arbitrary amount of keyword arguments, and store it in a dictionary. There can be no additional parameters after **kwargs in the parameter list.

Use cases
- Decorators (see /tag decorators)
- Inheritance (overriding methods)
- Future proofing (in the case of the first two bullet points, if the parameters change, your code won't break)
- Flexibility (writing functions that behave like dict() or print())

See /tag positional-keyword for information about positional and keyword arguments

async-await

Python provides the ability to run multiple tasks and coroutines simultaneously with the use of the asyncio library, which is included in the Python standard library.

This works by running these coroutines in an event loop, where the context of the running coroutine switches periodically to allow all other coroutines to run, thus giving the appearance of running at the same time. This is different to using threads or processes in that all code runs in the main process and thread, although it is possible to run coroutines in other threads.

To call an async function we can either await it, or run it in an event loop which we get from asyncio.

To create a coroutine that can be used with asyncio we need to define a function using the async keyword:

async def main():
    await something_awaitable()
Which means we can call await something_awaitable() directly from within the function. If this were a non-async function, it would raise the exception SyntaxError: 'await' outside async function

To run the top level async function from outside the event loop we need to use asyncio.run(), like this:

import asyncio

async def main():
    await something_awaitable()

asyncio.run(main())
Note that in the asyncio.run(), where we appear to be calling main(), this does not execute the code in main. Rather, it creates and returns a new coroutine object (i.e main() is not main()) which is then handled and run by the event loop via asyncio.run().

To learn more about asyncio and its use, see the asyncio documentation.

blocking

Imagine that you're coding a Discord bot and every time somebody uses a command, you need to get some information from a database. But there's a catch: the database servers are acting up today and take a whole 10 seconds to respond. If you do not use asynchronous methods, your whole bot will stop running until it gets a response from the database. How do you fix this? Asynchronous programming.

What is asynchronous programming? An asynchronous program utilises the async and await keywords. An asynchronous program pauses what it's doing and does something else whilst it waits for some third-party service to complete whatever it's supposed to do. Any code within an async context manager or function marked with the await keyword indicates to Python, that whilst this operation is being completed, it can do something else. For example:

import discord

# Bunch of bot code

async def ping(ctx):
    await ctx.send("Pong!")
What does the term "blocking" mean? A blocking operation is wherever you do something without awaiting it. This tells Python that this step must be completed before it can do anything else. Common examples of blocking operations, as simple as they may seem, include: outputting text, adding two numbers and appending an item onto a list. Most common Python libraries have an asynchronous version available to use in asynchronous contexts.

async libraries - The standard async library - asyncio - Asynchronous web requests - aiohttp - Talking to PostgreSQL asynchronously - asyncpg - MongoDB interactions asynchronously - motor - Check out this list for even more!

botvar

Python allows you to set custom attributes to most objects, like your bot! By storing things as attributes of the bot object, you can access them anywhere you access your bot. In the discord.py library, these custom attributes are commonly known as "bot variables" and can be a lifesaver if your bot is divided into many different files. An example on how to use custom attributes on your bot is shown below:

bot = commands.Bot(command_prefix="!")
# Set an attribute on our bot
bot.test = "I am accessible everywhere!"

@bot.command()
async def get(ctx: commands.Context):
    """A command to get the current value of `test`."""
    # Send what the test attribute is currently set to
    await ctx.send(ctx.bot.test)

@bot.command()
async def setval(ctx: commands.Context, *, new_text: str):
    """A command to set a new value of `test`."""
    # Here we change the attribute to what was specified in new_text
    bot.test = new_text

This all applies to cogs as well! You can set attributes to self as you wish.

Be sure not to overwrite attributes discord.py uses, like cogs or users. Name your attributes carefully!

class

Classes are used to create objects that have specific behavior.

Every object in Python has a class, including lists, dictionaries and even numbers. Using a class to group code and data like this is the foundation of Object Oriented Programming. Classes allow you to expose a simple, consistent interface while hiding the more complicated details. This simplifies the rest of your program and makes it easier to separately maintain and debug each component.

Here is an example class:

class Foo:
    def __init__(self, somedata):
        self.my_attrib = somedata

    def show(self):
        print(self.my_attrib)

To use a class, you need to instantiate it. The following creates a new object named bar, with Foo as its class.

bar = Foo('data')
bar.show()

We can access any of Foo's methods via bar.my_method(), and access any of bars data via bar.my_attribute.

classmethod

Although most methods are tied to an object instance, it can sometimes be useful to create a method that does something with the class itself. To achieve this in Python, you can use the @classmethod decorator. This is often used to provide alternative constructors for a class.

For example, you may be writing a class that takes some magic token (like an API key) as a constructor argument, but you sometimes read this token from a configuration file. You could make use of a @classmethod to create an alternate constructor for when you want to read from the configuration file.

class Bot:
    def __init__(self, token: str):
        self._token = token  

    @classmethod
    def from_config(cls, config: dict) -> Bot:
        token = config['token']
        return cls(token)

# now we can create the bot instance like this
alternative_bot = Bot.from_config(default_config)

# but this still works, too
regular_bot = Bot("tokenstring")
This is just one of the many use cases of @classmethod. A more in-depth explanation can be found here.

codeblock

Here's how to format Python code on Discord:

```py print('Hello world!') ```

These are backticks, not quotes. Check this out if you can't find the backtick key.

For long code samples, you can use our pastebin.

comparison

The assignment operator (=) is used to assign variables.

x = 5
print(x)  # Prints 5
The equality operator (==) is used to compare values.
if x == 5:
    print("The value of x is 5")

contribute

Looking to contribute to Open Source Projects for the first time? Want to add a feature or fix a bug on the bots on this server? We have on-going projects that people can contribute to, even if you've never contributed to open source before!

Projects to Contribute to - Sir Lancebot - our fun, beginner-friendly bot - Python - our utility & moderation bot - Site - resources, guides, and more

Where to start 1. Read our contribution guide 2. Chat with us in <#635950537262759947> if you're ready to jump in or have any questions 3. Open an issue or ask to be assigned to an issue to work on

customchecks

Often you may find the need to use checks that don't exist by default in discord.py. Fortunately, discord.py provides discord.ext.commands.check which allows you to create you own checks like this:

from discord.ext.commands import check, Context

def in_any_channel(*channels):
  async def predicate(ctx: Context):
    return ctx.channel.id in channels
  return check(predicate)
This check is to check whether the invoked command is in a given set of channels. The inner function, named predicate here, is used to perform the actual check on the command, and check logic should go in this function. It must be an async function, and always provides a single commands.Context argument which you can use to create check logic. This check function should return a boolean value indicating whether the check passed (return True) or failed (return False).

The check can now be used like any other commands check as a decorator of a command, such as this:

@bot.command(name="ping")
@in_any_channel(728343273562701984)
async def ping(ctx: Context):
  ...
This would lock the ping command to only be used in the channel 728343273562701984. If this check function fails it will raise a CheckFailure exception, which can be handled in your error handler.

customcooldown

Cooldowns can be used in discord.py to rate-limit. In this example, we're using it in an on_message.

from discord.ext import commands

message_cooldown = commands.CooldownMapping.from_cooldown(1.0, 60.0, commands.BucketType.user)

@bot.event
async def on_message(message):
    bucket = message_cooldown.get_bucket(message)
    retry_after = bucket.update_rate_limit()
    if retry_after:
        await message.channel.send(f"Slow down! Try again in {retry_after} seconds.")
    else:
        await message.channel.send("Not ratelimited!")

from_cooldown takes the amount of update_rate_limit()s needed to trigger the cooldown, the time in which the cooldown is triggered, and a BucketType.

customhelp

To learn more about how to create custom help commands in discord.py by subclassing the help command, please see this tutorial by Stella#2000

dashmpip

When trying to install a package via pip, it's recommended to invoke pip as a module: python -m pip install your_package.

Why would we use python -m pip instead of pip? Invoking pip as a module ensures you know which pip you're using. This is helpful if you have multiple Python versions. You always know which Python version you're installing packages to.

Note The exact python command you invoke can vary. It may be python3 or py, ensure it's correct for your system.

decorators

A decorator is a function that modifies another function.

Consider the following example of a timer decorator:

>>> import time
>>> def timer(f):
...     def inner(*args, **kwargs):
...         start = time.time()
...         result = f(*args, **kwargs)
...         print('Time elapsed:', time.time() - start)
...         return result
...     return inner
...
>>> @timer
... def slow(delay=1):
...     time.sleep(delay)
...     return 'Finished!'
...
>>> print(slow())
Time elapsed: 1.0011568069458008
Finished!
>>> print(slow(3))
Time elapsed: 3.000307321548462
Finished!

More information:
- Corey Schafer's video on decorators
- Real python article

defaultdict

The Python defaultdict type behaves almost exactly like a regular Python dictionary, but if you try to access or modify a missing key, the defaultdict will automatically insert the key and generate a default value for it. While instantiating a defaultdict, we pass in a function that tells it how to create a default value for missing keys.

>>> from collections import defaultdict
>>> my_dict = defaultdict(int)
>>> my_dict
defaultdict(<class 'int'>, {})

In this example, we've used the int class which returns 0 when called like a function, so any missing key will get a default value of 0. You can also get an empty list by default with list or an empty string with str.

>>> my_dict["foo"]
0
>>> my_dict["bar"] += 5
>>> my_dict
defaultdict(<class 'int'>, {'foo': 0, 'bar': 5})
Check out the docs to learn even more!

dict-get

Often while using dictionaries in Python, you may run into KeyErrors. This error is raised when you try to access a key that isn't present in your dictionary. Python gives you some neat ways to handle them.

The dict.get method will return the value for the key if it exists, and None (or a default value that you specify) if the key doesn't exist. Hence it will never raise a KeyError.

>>> my_dict = {"foo": 1, "bar": 2}
>>> print(my_dict.get("foobar"))
None
Below, 3 is the default value to be returned, because the key doesn't exist-
>>> print(my_dict.get("foobar", 3))
3
Some other methods for handling KeyErrors gracefully are the dict.setdefault method and collections.defaultdict (check out the !defaultdict tag).

dictcomps

Dictionary comprehensions (dict comps) provide a convenient way to make dictionaries, just like list comps:

>>> {word.lower(): len(word) for word in ('I', 'love', 'Python')}
{'i': 1, 'love': 4, 'python': 6}
The syntax is very similar to list comps except that you surround it with curly braces and have two expressions: one for the key and one for the value.

One can use a dict comp to change an existing dictionary using its items method

>>> first_dict = {'i': 1, 'love': 4, 'python': 6}
>>> {key.upper(): value * 2 for key, value in first_dict.items()}
{'I': 2, 'LOVE': 8, 'PYTHON': 12}
For more information and examples, check out PEP 274

discord-bot-hosting

Using free hosting options like repl.it or Heroku for continuous 24/7 bot hosting is strongly discouraged. Instead, opt for a virtual private server (VPS) or use your own spare hardware if you'd rather not pay for hosting.

See our Discord Bot Hosting Guide on our website that compares many hosting providers, both free and paid.

You may also use <#965291480992321536> to discuss different discord bot hosting options.

docstring

A docstring is a string - always using triple quotes - that's placed at the top of files, classes and functions. A docstring should contain a clear explanation of what it's describing. You can also include descriptions of the subject's parameter(s) and what it returns, as shown below:

def greet(name: str, age: int) -> str:
    """
    Return a string that greets the given person, using their name and age.

    :param name: The name of the person to greet.
    :param age: The age of the person to greet.

    :return: The greeting.
    """
    return f"Hello {name}, you are {age} years old!"
You can get the docstring by using the inspect.getdoc function, from the built-in inspect module, or by accessing the .__doc__ attribute. inspect.getdoc is often preferred, as it clears indents from the docstring.

For the last example, you can print it by doing this: print(inspect.getdoc(greet)).

For more details about what a docstring is and its usage, check out this guide by Real Python, or the official docstring specification.

dotenv

.env (dotenv) files are a type of file commonly used for storing application secrets and variables, for example API tokens and URLs, although they may also be used for storing other configurable values. While they are commonly used for storing secrets, at a high level their purpose is to load environment variables into a program.

Dotenv files are especially suited for storing secrets as they are a key-value store in a file, which can be easily loaded in most programming languages and ignored by version control systems like Git with a single entry in a .gitignore file.

In Python you can use dotenv files with the python-dotenv module from PyPI, which can be installed with pip install python-dotenv. To use dotenv files you'll first need a file called .env, with content such as the following:

TOKEN=a00418c85bff087b49f23923efe40aa5
Next, in your main Python file, you need to load the environment variables from the dotenv file you just created:
from dotenv import load_dotenv

load_dotenv()
The variables from the file have now been loaded into your program's environment, and you can access them using os.getenv() anywhere in your program, like this:
from os import getenv

my_token = getenv("TOKEN")
For further reading about tokens and secrets, please read this explanation.

dunder-methods

Double-underscore methods, or "dunder" methods, are special methods defined in a class that are invoked implicitly. Like the name suggests, they are prefixed and suffixed with dunders. You've probably already seen some, such as the __init__ dunder method, also known as the "constructor" of a class, which is implicitly invoked when you instantiate an instance of a class.

When you create a new class, there will be default dunder methods inherited from the object class. However, we can override them by redefining these methods within the new class. For example, the default __init__ method from object doesn't take any arguments, so we almost always override that to fit our needs.

Other common dunder methods to override are __str__ and __repr__. __repr__ is the developer-friendly string representation of an object - usually the syntax to recreate it - and is implicitly called on arguments passed into the repr function. __str__ is the user-friendly string representation of an object, and is called by the str function. Note here that, if not overriden, the default __str__ invokes __repr__ as a fallback.

class Foo:
    def __init__(self, value):  # constructor
        self.value = value
    def __str__(self):
        return f"This is a Foo object, with a value of {self.value}!"  # string representation
    def __repr__(self):
        return f"Foo({self.value!r})"  # way to recreate this object


bar = Foo(5)

# print also implicitly calls __str__
print(bar)  # Output: This is a Foo object, with a value of 5!

# dev-friendly representation
print(repr(bar))  # Output: Foo(5)

Another example: did you know that when you use the <left operand> + <right operand> syntax, you're implicitly calling <left operand>.__add__(<right operand>)? The same applies to other operators, and you can look at the operator built-in module documentation for more information!

empty-json

When using JSON, you might run into the following error:

JSONDecodeError: Expecting value: line 1 column 1 (char 0)
This error could have appeared because you just created the JSON file and there is nothing in it at the moment.

Whilst having empty data is no problem, the file itself may never be completely empty.

You most likely wanted to structure your JSON as a dictionary. To do this, edit your empty JSON file so that it instead contains {}.

Different data types are also supported. If you wish to read more on these, please refer to this article.

enumerate

Ever find yourself in need of the current iteration number of your for loop? You should use enumerate! Using enumerate, you can turn code that looks like this:

index = 0
for item in my_list:
    print(f"{index}: {item}")
    index += 1
into beautiful, pythonic code:
for index, item in enumerate(my_list):
    print(f"{index}: {item}")
For more information, check out the official docs, or PEP 279.

environments

The main purpose of Python virtual environments is to create an isolated environment for Python projects. This means that each project can have its own dependencies, such as third party packages installed using pip, regardless of what dependencies every other project has.

To see the current environment in use by Python, you can run:

>>> import sys
>>> sys.executable
'/usr/bin/python3'

To see the environment in use by pip, you can do pip debug (pip3 debug for Linux/macOS). The 3rd line of the output will contain the path in use e.g. sys.executable: /usr/bin/python3.

If Python's sys.executable doesn't match pip's, then they are currently using different environments! This may cause Python to raise a ModuleNotFoundError when you try to use a package you just installed with pip, as it was installed to a different environment.

Why use a virtual environment?

  • Resolve dependency issues by allowing the use of different versions of a package for different projects. For example, you could use Package A v2.7 for Project X and Package A v1.3 for Project Y.
  • Make your project self-contained and reproducible by capturing all package dependencies in a requirements file. Try running pip freeze to see what you currently have installed!
  • Keep your global site-packages/ directory tidy by removing the need to install packages system-wide which you might only need for one project.

Further reading:

equals-true

It's tempting to think that if statements always need a comparison operator like == or !=, but this isn't true. If you're just checking if a value is truthy or falsey, you don't need == True or == False.

# instead of this...
if user_input.startswith('y') == True:
    my_func(user_input)

# ...write this
if user_input.startswith('y'):
    my_func(user_input)

# for false conditions, instead of this...
if user_input.startswith('y') == False:
    my_func(user_input)

# ...just use `not`
if not user_input.startswith('y'):
    my_func(user_input)
This also applies to expressions that use is True or is False.

except

A key part of the Python philosophy is to ask for forgiveness, not permission. This means that it's okay to write code that may produce an error, as long as you specify how that error should be handled. Code written this way is readable and resilient.

try:
    number = int(user_input)
except ValueError:
    print("failed to convert user_input to a number. setting number to 0.")
    number = 0
You should always specify the exception type if it is possible to do so, and your try block should be as short as possible. Attempting to handle broad categories of unexpected exceptions can silently hide serious problems.
try:
    number = int(user_input)
    item = some_list[number]
except:
    print("An exception was raised, but we have no idea if it was a ValueError or an IndexError.")
For more information about exception handling, see the official Python docs, or watch Corey Schafer's video on exception handling.

exit()

If you want to exit your code programmatically, you might think to use the functions exit() or quit(), however this is bad practice. These functions are constants added by the site module as a convenient method for exiting the interactive interpreter shell, and should not be used in programs.

You should use either SystemExit or sys.exit() instead. There's not much practical difference between these two other than having to import sys for the latter. Both take an optional argument to provide an exit status.

Official documentation with the warning not to use exit() or quit() in source code.

f-strings

Creating a Python string with your variables using the + operator can be difficult to write and read. F-strings (format-strings) make it easy to insert values into a string. If you put an f in front of the first quote, you can then put Python expressions between curly braces in the string.

>>> snake = "pythons"
>>> number = 21
>>> f"There are {number * 2} {snake} on the plane."
"There are 42 pythons on the plane."
Note that even when you include an expression that isn't a string, like number * 2, Python will convert it to a string for you.

faq

As the largest Python community on Discord, we get hundreds of questions every day. Many of these questions have been asked before. We've compiled a list of the most frequently asked questions along with their answers, which can be found on our FAQ page.

floats

You may have noticed that when doing arithmetic with floats in Python you sometimes get strange results, like this:

>>> 0.1 + 0.2
0.30000000000000004
Why this happens Internally your computer stores floats as binary fractions. Many decimal values cannot be stored as exact binary fractions, which means an approximation has to be used.

How you can avoid this You can use math.isclose to check if two floats are close, or to get an exact decimal representation, you can use the decimal or fractions module. Here are some examples:

>>> math.isclose(0.1 + 0.2, 0.3)
True
>>> decimal.Decimal('0.1') + decimal.Decimal('0.2')
Decimal('0.3')
Note that with decimal.Decimal we enter the number we want as a string so we don't pass on the imprecision from the float.

For more details on why this happens check out this page in the python docs or this Computerphile video.

foo

A specific word or set of words identified as a placeholder used in programming. They are used to name entities such as variables, functions, etc, whose exact identity is unimportant and serve only to demonstrate a concept, which is useful for teaching programming.

Common examples include foobar, foo, bar, baz, and qux. Python has its own metasyntactic variables, namely spam, eggs, and bacon. This is a reference to a Monty Python sketch (the eponym of the language).

More information:
- History of foobar
- Monty Python sketch

for-else

In Python it's possible to attach an else clause to a for loop. The code under the else block will be run when the iterable is exhausted (there are no more items to iterate over). Code within the else block will not run if the loop is broken out using break.

Here's an example of its usage:

numbers = [1, 3, 5, 7, 9, 11]

for number in numbers:
    if number % 2 == 0:
        print(f"Found an even number: {number}")
        break
    print(f"{number} is odd.")
else:
    print("All numbers are odd. How odd.")
Try running this example but with an even number in the list, see how the output changes as you do so.

functions-are-objects

When assigning a new name to a function, storing it in a container, or passing it as an argument, a common mistake made is to call the function. Instead of getting the actual function, you'll get its return value.

In Python you can treat function names just like any other variable. Assume there was a function called now that returns the current time. If you did x = now(), the current time would be assigned to x, but if you did x = now, the function now itself would be assigned to x. x and now would both equally reference the function.

Examples

# assigning new name

def foo():
    return 'bar'

def spam():
    return 'eggs'

baz = foo
baz() # returns 'bar'

ham = spam
ham() # returns 'eggs'
# storing in container

import math
functions = [math.sqrt, math.factorial, math.log]
functions[0](25) # returns 5.0
# the above equivalent to math.sqrt(25)
# passing as argument

class C:
    builtin_open = staticmethod(open)

# open function is passed
# to the staticmethod class

global

When adding functions or classes to a program, it can be tempting to reference inaccessible variables by declaring them as global. Doing this can result in code that is harder to read, debug and test. Instead of using globals, pass variables or objects as parameters and receive return values.

Instead of writing

def update_score():
    global score, roll
    score = score + roll
update_score()
do this instead
def update_score(score, roll):
    return score + roll
score = update_score(score, roll)
For in-depth explanations on why global variables are bad news in a variety of situations, see this Stack Overflow answer.

guilds

The communities page on our website contains a number of communities we have partnered with as well as a curated list of other communities relating to programming and technology.

identity

Should I be using is or ==?

To check if two objects are equal, use the equality operator (==).

x = 5
if x == 5:
    print("x equals 5")
if x == 3:
    print("x equals 3")
# Prints 'x equals 5'
To check if two objects are actually the same thing in memory, use the identity comparison operator (is).
>>> list_1 = [1, 2, 3]
>>> list_2 = [1, 2, 3]
>>> if list_1 is [1, 2, 3]:
...    print("list_1 is list_2")
...
>>> reference_to_list_1 = list_1
>>> if list_1 is reference_to_list_1:
...    print("list_1 is reference_to_list_1")
...
list_1 is reference_to_list_1

if-name-main

This is a statement that is only true if the module (your source code) it appears in is being run directly, as opposed to being imported into another module. When you run your module, the __name__ special variable is automatically set to the string '__main__'. Conversely, when you import that same module into a different one, and run that, __name__ is instead set to the filename of your module minus the .py extension.

Example

# foo.py

print('spam')

if __name__ == '__main__':
    print('eggs')
If you run the above module foo.py directly, both 'spam'and 'eggs' will be printed. Now consider this next example:
# bar.py

import foo
If you run this module named bar.py, it will execute the code in foo.py. First it will print 'spam', and then the if statement will fail, because __name__ will now be the string 'foo'.

Why would I do this?

  • Your module is a library, but also has a special case where it can be run directly
  • Your module is a library and you want to safeguard it against people running it directly (like what pip does)
  • Your module is the main program, but has unit tests and the testing framework works by importing your module, and you want to avoid having your main code run during the test
in-place

In programming, there are two types of operations: - "In-place" operations, which modify the original object - "Out-of-place" operations, which returns a new object and leaves the original object unchanged

For example, the .sort() method of lists is in-place, so it modifies the list you call .sort() on:

>>> my_list = [5, 2, 3, 1]
>>> my_list.sort()  # Returns None
>>> my_list
[1, 2, 3, 5]
On the other hand, the sorted() function is out-of-place, so it returns a new list and leaves the original list unchanged:
>>> my_list = [5, 2, 3, 1]
>>> sorted_list = sorted(my_list)
>>> sorted_list
[1, 2, 3, 5]
>>> my_list
[5, 2, 3, 1]
In general, methods of mutable objects tend to be in-place (since it can be expensive to create a new object), whereas operations on immutable objects are always out-of-place (since they cannot be modified).

indent

Indentation is leading whitespace (spaces and tabs) at the beginning of a line of code. In the case of Python, they are used to determine the grouping of statements.

Spaces should be preferred over tabs. To be clear, this is in reference to the character itself, not the keys on a keyboard. Your editor/IDE should be configured to insert spaces when the TAB key is pressed. The amount of spaces should be a multiple of 4, except optionally in the case of continuation lines.

Example

def foo():
    bar = 'baz'  # indented one level
    if bar == 'baz':
        print('ham')  # indented two levels
    return bar  # indented one level
The first line is not indented. The next two lines are indented to be inside of the function definition. They will only run when the function is called. The fourth line is indented to be inside the if statement, and will only run if the if statement evaluates to True. The fifth and last line is like the 2nd and 3rd and will always run when the function is called. It effectively closes the if statement above as no more lines can be inside the if statement below that line.

Indentation is used after: 1. Compound statements (eg. if, while, for, try, with, def, class, and their counterparts) 2. Continuation lines

More Info 1. Indentation style guide 2. Tabs or Spaces? 3. Official docs on indentation

inline

Inline codeblocks look like this. To create them you surround text with single backticks, so `hello` would become hello.

Note that backticks are not quotes, see this if you are struggling to find the backtick key.

If the wrapped code itself has a backtick, wrap it with two backticks from each side: ``back ` tick`` would become back ` tick.

For how to make multiline codeblocks see the !codeblock tag.

intents

Intents are a feature of Discord that tells the gateway exactly which events to send your bot. Various features of discord.py rely on having particular intents enabled, further detailed in its documentation. Since discord.py v2.0.0, it has become mandatory for developers to explicitly define the values of these intents in their code.

There are standard and privileged intents. To use privileged intents like Presences, Server Members, and Message Content, you have to first enable them in the Discord Developer Portal. In there, go to the Bot page of your application, scroll down to the Privileged Gateway Intents section, and enable the privileged intents that you need. Standard intents can be used without any changes in the developer portal.

Afterwards in your code, you need to set the intents you want to connect with in the bot's constructor using the intents keyword argument, like this:

from discord import Intents
from discord.ext import commands

# Enable all standard intents and message content
# (prefix commands generally require message content)
intents = Intents.default()
intents.message_content = True

bot = commands.Bot(command_prefix="!", intents=intents)
For more info about using intents, see discord.py's related guide, and for general information about them, see the Discord developer documentation on intents.

iterate-dict

There are two common ways to iterate over a dictionary in Python. To iterate over the keys:

for key in my_dict:
    print(key)
To iterate over both the keys and values:
for key, val in my_dict.items():
    print(key, val)

kindling-projects

The Kindling projects page on Ned Batchelder's website contains a list of projects and ideas programmers can tackle to build their skills and knowledge.

listcomps

Do you ever find yourself writing something like this?

>>> squares = []
>>> for n in range(5):
...    squares.append(n ** 2)
[0, 1, 4, 9, 16]
Using list comprehensions can make this both shorter and more readable. As a list comprehension, the same code would look like this:
>>> [n ** 2 for n in range(5)]
[0, 1, 4, 9, 16]
List comprehensions also get an if clause:
>>> [n ** 2 for n in range(5) if n % 2 == 0]
[0, 4, 16]

For more info, see this pythonforbeginners.com post.

local-file

Thanks to discord.py, sending local files as embed images is simple. You have to create an instance of discord.File class:

# When you know the file exact path, you can pass it.
file = discord.File("/this/is/path/to/my/file.png", filename="file.png")

# When you have the file-like object, then you can pass this instead path.
with open("/this/is/path/to/my/file.png", "rb") as f:
    file = discord.File(f)
When using the file-like object, you have to open it in rb ('read binary') mode. Also, in this case, passing filename to it is not necessary. Please note that filename must not contain underscores. This is a Discord limitation.

discord.Embed instances have a set_image method which can be used to set an attachment as an image:

embed = discord.Embed()
# Set other fields
embed.set_image(url="attachment://file.png")  # Filename here must be exactly same as attachment filename.
After this, you can send an embed with an attachment to Discord:
await channel.send(file=file, embed=embed)
This example uses discord.TextChannel for sending, but any instance of discord.abc.Messageable can be used for sending.

loop-remove

Avoid adding to or removing from a collection, such as a list, as you iterate that collection in a for loop:

data = [1, 2, 3, 4]
for item in data:
    data.remove(item)
print(data)  # [2, 4] <-- every OTHER item was removed!
Inside the loop, an index tracks the current position. If the list is modified, this index may no longer refer to the same element, causing elements to be repeated or skipped.

In the example above, 1 is removed, shifting 2 to index 0. The loop then moves to index 1, removing 3 (and skipping 2!).

You can avoid this pitfall by: - using a list comprehension to produce a new list (as a way of filtering items):

data = [x for x in data if x % 2 == 0]
- using a while loop and .pop() (treating the list as a stack):
while data:
    item = data.pop()
- considering whether you need to remove items in the first place!

message-content-intent

The Discord gateway only dispatches events you subscribe to, which you can configure by using "intents."

The message content intent is what determines if an app will receive the actual content of newly created messages. Without this intent, discord.py won't be able to detect prefix commands, so prefix commands won't respond.

Privileged intents, such as message content, have to be explicitly enabled from the Discord Developer Portal in addition to being enabled in the code:

intents = discord.Intents.default() # create a default Intents instance
intents.message_content = True # enable message content intents

bot = commands.Bot(command_prefix="!", intents=intents) # actually pass it into the constructor
For more information on intents, see /tag intents. If prefix commands are still not working, see /tag on-message-event.

microsoft-build-tools

When you install a library through pip on Windows, sometimes you may encounter this error:

error: Microsoft Visual C++ 14.0 or greater is required. Get it with "Microsoft C++ Build Tools": https://visualstudio.microsoft.com/visual-cpp-build-tools/

This means the library you're installing has code written in other languages and needs additional tools to install. To install these tools, follow the following steps: (Requires 6GB+ disk space)

1. Open https://visualstudio.microsoft.com/visual-cpp-build-tools/. 2. Click Download Build Tools >. A file named vs_BuildTools or vs_BuildTools.exe should start downloading. If no downloads start after a few seconds, click click here to retry. 3. Run the downloaded file. Click Continue to proceed. 4. Choose C++ build tools and press Install. You may need a reboot after the installation. 5. Try installing the library via pip again.

modmail

<@!683001325440860340> is a bot that will relay your messages to our moderation team, so that you can start a conversation with the moderation team. Your messages will be relayed to the entire moderator team, who will be able to respond to you via the bot.

It supports attachments, codeblocks, and reactions. As communication happens over direct messages, the conversation will stay between you and the mod team.

To use it, simply send a direct message to the bot.

Should there be an urgent and immediate need for a moderator to look at a channel, feel free to ping the <@&831776746206265384> role instead.

mutability

Imagine that you want to make all letters in a string upper case. Conveniently, strings have an .upper() method.

You might think that this would work:

>>> greeting = "hello"
>>> greeting.upper()
'HELLO'
>>> greeting
'hello'

greeting didn't change. Why is that so?

That's because strings in Python are immutable. You can't change them, you can only pass around existing strings or create new ones.

>>> greeting = "hello"
>>> greeting = greeting.upper()
>>> greeting
'HELLO'

greeting.upper() creates and returns a new string which is like the old one, but with all the letters turned to upper case.

int, float, complex, tuple, frozenset are other examples of immutable data types in Python.

Mutable data types like list, on the other hand, can be changed in-place:

>>> my_list = [1, 2, 3]
>>> my_list.append(4)
>>> my_list
[1, 2, 3, 4]

Other examples of mutable data types in Python are dict and set. Instances of user-defined classes are also mutable.

For an in-depth guide on mutability see Ned Batchelder's video on names and values.

mutable-default-args

Default arguments in Python are evaluated once when the function is defined, not each time the function is called. This means that if you have a mutable default argument and mutate it, you will have mutated that object for all future calls to the function as well.

For example, the following append_one function appends 1 to a list and returns it. foo is set to an empty list by default.

>>> def append_one(foo=[]):
...     foo.append(1)
...     return foo
...
See what happens when we call it a few times:
>>> append_one()
[1]
>>> append_one()
[1, 1]
>>> append_one()
[1, 1, 1]
Each call appends an additional 1 to our list foo. It does not receive a new empty list on each call, it is the same list everytime.

To avoid this problem, you have to create a new object every time the function is called:

>>> def append_one(foo=None):
...     if foo is None:
...         foo = []
...     foo.append(1)
...     return foo
...
>>> append_one()
[1]
>>> append_one()
[1]

Note:

  • This behavior can be used intentionally to maintain state between calls of a function (eg. when writing a caching function).
  • This behavior is not unique to mutable objects, all default arguments are evaulated only once when the function is defined.
names

A name is a piece of text that is bound to an object. They are a reference to an object. Examples are function names, class names, module names, variables, etc.

Note: Names cannot reference other names, and assignment never creates a copy.

x = 1  # x is bound to 1
y = x  # y is bound to VALUE of x
x = 2  # x is bound to 2
print(x, y) # 2 1
When doing y = x, the name y is being bound to the value of x which is 1. Neither x nor y are the 'real' name. The object 1 simply has multiple names. They are the exact same object.
>>> x = 1
x ━━ 1

>>> y = x
x ━━ 1
y ━━━┛

>>> x = 2
x ━━ 2
y ━━ 1
Names are created in multiple ways
You might think that the only way to bind a name to an object is by using assignment, but that isn't the case. All of the following work exactly the same as assignment:
- import statements
- class and def
- for loop headers
- as keyword when used with except, import, and with
- formal parameters in function headers

There is also del which has the purpose of unbinding a name.

More info
- Please watch Ned Batchelder's talk on names in python for a detailed explanation with examples
- Official documentation

nomodule

If you've installed a package but you're getting a ModuleNotFoundError when you try to import it, it's likely that the environment where your code is running is different from the one where you did the installation.

You can read about Python environments at /tag environments and /tag venv.

Common causes of this problem include:

  • You installed your package using pip install .... It could be that the pip command is not pointing to the environment where your code runs. For greater control, you could instead run pip as a module within the python environment you specify:
    python -m pip install <your_package>
  • Your editor/ide is configured to create virtual environments automatically (PyCharm is configured this way by default).
off-topic-names

There are three off-topic channels: - <#291284109232308226> - <#463035241142026251> - <#463035268514185226>

The channel names change every night at midnight UTC and are often fun meta references to jokes or conversations that happened on the server.

See our off-topic etiquette page for more guidance on how the channels should be used.

on-message-event

Registering the on_message event with @bot.event will override the default behavior of the event. This may cause prefix commands to stop working, because they rely on the default on_message event handler.

Instead, use @bot.listen to add a listener. Listeners get added alongside the default on_message handler which allows you to have multiple handlers for the same event. This means prefix commands can still be invoked as usual. Here's an example:

@bot.listen()
async def on_message(message):
    ...  # do stuff here

# Or...

@bot.listen('on_message')
async def message_listener(message):
    ...  # do stuff here
You can also tell discord.py to process the message for commands as usual at the end of the on_message handler with bot.process_commands(). However, this method isn't recommended as it does not allow you to add multiple on_message handlers.

If your prefix commands are still not working, it may be because you haven't enabled the message_content intent. See /tag message_content for more info.

open

The built-in function open() is one of several ways to open files on your computer. It accepts many different parameters, so this tag will only go over two of them (file and mode). For more extensive documentation on all these parameters, consult the official documentation. The object returned from this function is a file object or stream, for which the full documentation can be found here.

See also:
- !tags with for information on context managers
- !tags pathlib for an alternative way of opening files
- !tags seek for information on changing your position in a file

The file parameter

This should be a path-like object denoting the name or path (absolute or relative) to the file you want to open.

An absolute path is the full path from your root directory to the file you want to open. Generally this is the option you should choose so it doesn't matter what directory you're in when you execute your module.

See !tags relative-path for more information on relative paths.

The mode parameter

This is an optional string that specifies the mode in which the file should be opened. There's not enough room to discuss them all, but listed below are some of the more confusing modes.

  • 'r+' Opens for reading and writing (file must already exist)
  • 'w+' Opens for reading and writing and truncates (can create files)
  • 'x' Creates file and opens for writing (file must not already exist)
  • 'x+' Creates file and opens for reading and writing (file must not already exist)
  • 'a+' Opens file for reading and writing at end of file (can create files)
or-gotcha

When checking if something is equal to one thing or another, you might think that this is possible:

# Incorrect...
if favorite_fruit == 'grapefruit' or 'lemon':
    print("That's a weird favorite fruit to have.")
While this makes sense in English, it may not behave the way you would expect. In Python, you should have complete instructions on both sides of the logical operator.

So, if you want to check if something is equal to one thing or another, there are two common ways:

# Like this...
if favorite_fruit == 'grapefruit' or favorite_fruit == 'lemon':
    print("That's a weird favorite fruit to have.")

# ...or like this.
if favorite_fruit in ('grapefruit', 'lemon'):
    print("That's a weird favorite fruit to have.")

ot

<#463035268514185226>

Please read our off-topic etiquette before participating in conversations.

param-arg

A parameter is a variable defined in a function signature (the line with def in it), while arguments are objects passed to a function call.

def square(n): # n is the parameter
    return n*n

print(square(5)) # 5 is the argument

Note that 5 is the argument passed to square, but square(5) in its entirety is the argument passed to print

paste

If your code is too long to fit in a codeblock in Discord, you can paste your code here: https://paste.pythondiscord.com/

After pasting your code, save it by clicking the Paste! button in the bottom left, or by pressing CTRL + S. After doing that, you will be navigated to the new paste's page. Copy the URL and post it here so others can see it.

pathlib

Python 3 comes with a new module named Pathlib. Since Python 3.6, pathlib.Path objects work nearly everywhere that os.path can be used, meaning you can integrate your new code directly into legacy code without having to rewrite anything. Pathlib makes working with paths way simpler than os.path does.

Feature spotlight:

  • Normalizes file paths for all platforms automatically
  • Has glob-like utilites (eg. Path.glob, Path.rglob) for searching files
  • Can read and write files, and close them automatically
  • Convenient syntax, utilising the / operator (e.g. Path('~') / 'Documents')
  • Can easily pick out components of a path (eg. name, parent, stem, suffix, anchor)
  • Supports method chaining
  • Move and delete files
  • And much more

More Info:

pep8

PEP 8 is the official style guide for Python. It includes comprehensive guidelines for code formatting, variable naming, and making your code easy to read. Professional Python developers are usually required to follow the guidelines, and will often use code-linters like flake8 to verify that the code they're writing complies with the style guide.

More information: - PEP 8 document - Our PEP 8 song! :notes:

positional-keyword

Functions can take two different kinds of arguments. A positional argument is just the object itself. A keyword argument is a name assigned to an object.

Example

>>> print('Hello', 'world!', sep=', ')
Hello, world!
The first two strings 'Hello' and 'world!' are positional arguments. The sep=', ' is a keyword argument.

Note A keyword argument can be passed positionally in some cases.

def sum(a, b=1):
    return a + b

sum(1, b=5)
sum(1, 5) # same as above
Sometimes this is forced, in the case of the pow() function.

The reverse is also true:

>>> def foo(a, b):
...     print(a, b)
...
>>> foo(a=1, b=2)
1 2
>>> foo(b=1, a=2)
2 1

More info
- Keyword only arguments
- Positional only arguments
- /tag param-arg (Parameters vs. Arguments)

precedence

Operator precedence is essentially like an order of operations for Python's operators.

Example 1 (arithmetic) 2 * 3 + 1 is 7 because multiplication is first 2 * (3 + 1) is 8 because the parenthesis change the precedence allowing the sum to be first

Example 2 (logic) not True or True is True because the not is first not (True or True) is False because the or is first

The full table of precedence from lowest to highest is here

quotes

Single and Double quoted strings are the same in Python. The choice of which one to use is up to you, just make sure that you stick to that choice.

With that said, there are exceptions to this that are more important than consistency. If a single or double quote is needed inside the string, using the opposite quotation is better than using escape characters.

Example:

'My name is "Guido"'   # good
"My name is \"Guido\"" # bad

"Don't go in there"  # good
'Don\'t go in there' # bad
Note: If you need both single and double quotes inside your string, use the version that would result in the least amount of escapes. In the case of a tie, use the quotation you use the most.

References:
- pep-8 on quotes
- convention for triple quoted strings

range-len

Beginners often iterate over range(len(...)) because they look like Java or C-style loops, but this is almost always a bad practice in Python.

for i in range(len(my_list)):
    do_something(my_list[i])
It's much simpler to iterate over the list (or other sequence) directly:
for item in my_list:
    do_something(item)
Python has other solutions for cases when the index itself might be needed. To get the element at the same index from two or more lists, use zip. To get both the index and the element at that index, use enumerate.

regex

Regular expressions (regex) are a tool for finding patterns in strings. The standard library's re module defines functions for using regex patterns.

Example We can use regex to pull out all the numbers in a sentence:

>>> import re
>>> text = "On Oct 18 1963 a cat was launched aboard rocket #47"
>>> regex_pattern = r"[0-9]{1,3}"  # Matches 1-3 digits
>>> re.findall(regex_pattern, text)
['18', '196', '3', '47']  # Notice the year is cut off
See Also - The re docs - for functions that use regex - regex101.com - an interactive site for testing your regular expression

relative-path

A relative path is a partial path that is relative to your current working directory. A common misconception is that your current working directory is the location of the module you're executing, but this is not the case. Your current working directory is actually the directory you were in when you ran the Python interpreter. The reason for this misconception is because a common way to run your code is to navigate to the directory your module is stored, and run python <module>.py. Thus, in this case your current working directory will be the same as the location of the module. However, if we instead did python path/to/<module>.py, our current working directory would no longer be the same as the location of the module we're executing.

Why is this important?

When opening files in Python, relative paths won't always work since it's dependent on what directory you were in when you ran your code. A common issue people face is running their code in an IDE thinking they can open files that are in the same directory as their module, but the current working directory will be different than what they expect and so they won't find the file. The way to avoid this problem is by using absolute paths, which is the full path from your root directory to the file you want to open.

repl

A REPL is an interactive shell where you can execute individual lines of code one at a time, like so:

>>> x = 5
>>> x + 2
7
>>> for i in range(3):
...     print(i)
...
0
1
2
>>>
To enter the REPL, run python (py on Windows) in the command line without any arguments. The >>> or ... at the start of some lines are prompts to enter code, and indicate that you are in the Python REPL. Any other lines show the output of the code.

Trying to execute commands for the command-line (such as pip install xyz) in the REPL will throw an error. To run these commands, exit the REPL first by running exit() and then run the original command.

return

A value created inside a function can't be used outside of it unless you return it.

Consider the following function:

def square(n):
    return n * n
If we wanted to store 5 squared in a variable called x, we would do: x = square(5). x would now equal 25.

Common Mistakes

>>> def square(n):
...     n * n  # calculates then throws away, returns None
...
>>> x = square(5)
>>> print(x)
None
>>> def square(n):
...     print(n * n)  # calculates and prints, then throws away and returns None
...
>>> x = square(5)
25
>>> print(x)
None
Things to note
- print() and return do not accomplish the same thing. print() will show the value, and then it will be gone.
- A function will return None if it ends without a return statement.
- When you want to print a value from a function, it's best to return the value and print the function call instead, like print(square(5)).

return-gif

Here's a handy animation demonstrating how print and return differ in behavior.

See also: /tag return

round

Python 3 uses bankers' rounding (also known by other names), where if the fractional part of a number is .5, it's rounded to the nearest even result instead of away from zero.

Example:

>>> round(2.5)
2
>>> round(1.5)
2
In the first example, there is a tie between 2 and 3, and since 3 is odd and 2 is even, the result is 2. In the second example, the tie is between 1 and 2, and so 2 is also the result.

Why this is done: The round half up technique creates a slight bias towards the larger number. With a large amount of calculations, this can be significant. The round half to even technique eliminates this bias.

It should be noted that round half to even distorts the distribution by increasing the probability of evens relative to odds, however this is considered less important than the bias explained above.

References:
- Wikipedia article about rounding
- Documentation on round function
- round in what's new in python 3 (4th bullet down)
- How to force rounding technique

scope

A scope defines the visibility of a name within a block, where a block is a piece of Python code executed as a unit. For simplicity, this would be a module, a function body, and a class definition. A name refers to text bound to an object.

For more information about names, see /tag names

A module is the source code file itself, and encompasses all blocks defined within it. Therefore if a variable is defined at the module level (top-level code block), it is a global variable and can be accessed anywhere in the module as long as the block in which it's referenced is executed after it was defined.

Alternatively if a variable is defined within a function block for example, it is a local variable. It is not accessible at the module level, as that would be outside its scope. This is the purpose of the return statement, as it hands an object back to the scope of its caller. Conversely if a function was defined inside the previously mentioned block, it would have access to that variable, because it is within the first function's scope.

>>> def outer():
...     foo = 'bar'     # local variable to outer
...     def inner():
...         print(foo)  # has access to foo from scope of outer
...     return inner    # brings inner to scope of caller
...
>>> inner = outer()  # get inner function
>>> inner()  # prints variable foo without issue
bar
Official Documentation
1. Program structure, name binding and resolution
2. global statement
3. nonlocal statement

seek

In the context of a file object, the seek function changes the stream position to a given byte offset, with an optional argument of where to offset from. While you can find the official documentation here, it can be unclear how to actually use this feature, so keep reading to see examples on how to use it.

File named example:

foobar
spam eggs
Open file for reading in byte mode:
f = open('example', 'rb')
Note that stream positions start from 0 in much the same way that the index for a list does. If we do f.seek(3, 0), our stream position will move 3 bytes forward relative to the beginning of the stream. Now if we then did f.read(1) to read a single byte from where we are in the stream, it would return the string 'b' from the 'b' in 'foobar'. Notice that the 'b' is the 4th character. Also note that after we did f.read(1), we moved the stream position again 1 byte forward relative to the current position in the stream. So the stream position is now currently at position 4.

Now lets do f.seek(4, 1). This will move our stream position 4 bytes forward relative to our current position in the stream. Now if we did f.read(1), it would return the string 'p' from the 'p' in 'spam' on the next line. Note this time that the character at position 6 is the newline character '\n'.

Finally, lets do f.seek(-4, 2), moving our stream position backwards 4 bytes relative to the end of the stream. Now if we did f.read() to read everything after our position in the file, it would return the string 'eggs' and also move our stream position to the end of the file.

Note
- For the second argument in seek(), use os.SEEK_SET, os.SEEK_CUR, and os.SEEK_END in place of 0, 1, and 2 respectively.
- os.SEEK_CUR is only usable when the file is in byte mode.

self

When calling a method from a class instance (ie. instance.method()), the instance itself will automatically be passed as the first argument implicitly. By convention, we call this self, but it could technically be called any valid variable name.

class Foo:
    def bar(self):
        print('bar')

    def spam(self, eggs):
        print(eggs)

foo = Foo()

If we call foo.bar(), it is equivalent to doing Foo.bar(foo). Our instance foo is passed for us to the bar function, so while we initially gave zero arguments, it is actually called with one.

Similarly if we call foo.spam('ham'), it is equivalent to doing Foo.spam(foo, 'ham').

Why is this useful?

Methods do not inherently have access to attributes defined in the class. In order for any one method to be able to access other methods or variables defined in the class, it must have access to the instance.

Consider if outside the class, we tried to do this: spam(foo, 'ham'). This would give an error, because we don't have access to the spam method directly, we have to call it by doing foo.spam('ham'). This is also the case inside of the class. If we wanted to call the bar method inside the spam method, we'd have to do self.bar(), just doing bar() would give an error.

site

Our official website is an open-source community project created with Python and Django. It contains information about the server itself, lets you sign up for upcoming events, has its own wiki, contains a list of valuable learning resources, and much more.

slicing

Slicing is a way of accessing a part of a sequence by specifying a start, stop, and step. As with normal indexing, negative numbers can be used to count backwards.

Examples

>>> letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g']
>>> letters[2:]  # from element 2 to the end
['c', 'd', 'e', 'f', 'g']
>>> letters[:4]  # up to element 4
['a', 'b', 'c', 'd']
>>> letters[3:5]  # elements 3 and 4 -- the right bound is not included
['d', 'e']
>>> letters[2:-1:2]  # Every other element between 2 and the last
['c', 'e']
>>> letters[::-1]  # The whole list in reverse
['g', 'f', 'e', 'd', 'c', 'b', 'a']
>>> words = "Hello world!"
>>> words[2:7]  # Strings are also sequences
"llo w"

sql-fstring

Don't use f-strings (f"") or other forms of "string interpolation" (%, +, .format) to inject data into a SQL query. It is an endless source of bugs and syntax errors. Additionally, in user-facing applications, it presents a major security risk via SQL injection.

Your database library should support "query parameters". A query parameter is a placeholder that you put in the SQL query. When the query is executed, you provide data to the database library, and the library inserts the data into the query for you, safely.

For example, the sqlite3 package supports using ? as a placeholder:

query = "SELECT * FROM stocks WHERE symbol = ?;"
params = ("RHAT",)
db.execute(query, params)
Note: Different database libraries support different placeholder styles, e.g. %s and $1. Consult your library's documentation for details.

See Also - Python sqlite3 docs - How to use placeholders to bind values in SQL queries - PEP-249 - A specification of how database libraries in Python should work

star-imports

Wildcard imports are import statements in the form from <module_name> import *. What imports like these do is that they import everything [1] from the module into the current module's namespace [2]. This allows you to use names defined in the imported module without prefixing the module's name.

Example:

>>> from math import *
>>> sin(pi / 2)
1.0
This is discouraged, for various reasons:

Example:

>>> from custom_sin import sin
>>> from math import *
>>> sin(pi / 2)  # uses sin from math rather than your custom sin
- Potential namespace collision. Names defined from a previous import might get shadowed by a wildcard import. - Causes ambiguity. From the example, it is unclear which sin function is actually being used. From the Zen of Python [3]: Explicit is better than implicit. - Makes import order significant, which they shouldn't. Certain IDE's sort import functionality may end up breaking code due to namespace collision.

How should you import?

  • Import the module under the module's namespace (Only import the name of the module, and names defined in the module can be used by prefixing the module's name)
    >>> import math
    >>> math.sin(math.pi / 2)
  • Explicitly import certain names from the module
    >>> from math import sin, pi
    >>> sin(pi / 2)
    Conclusion: Namespaces are one honking great idea -- let's do more of those! [3]

[1] If the module defines the variable __all__, the names defined in __all__ will get imported by the wildcard import, otherwise all the names in the module get imported (except for names with a leading underscore) [2] Namespaces and scopes [3] Zen of Python

str-join

If you want to display a list (or some other iterable), you can write:

colors = ['red', 'green', 'blue', 'yellow']
output = ""
separator = ", "
for color in colors:
    output += color + separator
print(output)
# Prints 'red, green, blue, yellow, '
However, the separator is still added to the last element, and it is relatively slow.

A better solution is to use str.join.

colors = ['red', 'green', 'blue', 'yellow']
separator = ", "
print(separator.join(colors))
# Prints 'red, green, blue, yellow'
An important thing to note is that you can only str.join strings. For a list of ints, you must convert each element to a string before joining.
integers = [1, 3, 6, 10, 15]
print(", ".join(str(e) for e in integers))
# Prints '1, 3, 6, 10, 15'

string-formatting

The String Formatting Language in Python is a powerful way to tailor the display of strings and other data structures. This string formatting mini language works for f-strings and .format().

Take a look at some of these examples!

>>> my_num = 2134234523
>>> print(f"{my_num:,}")
2,134,234,523

>>> my_smaller_num = -30.0532234
>>> print(f"{my_smaller_num:=09.2f}")
-00030.05

>>> my_str = "Center me!"
>>> print(f"{my_str:-^20}")
-----Center me!-----

>>> repr_str = "Spam \t Ham"
>>> print(f"{repr_str!r}")
'Spam \t Ham'
Full Specification & Resources String Formatting Mini Language Specification pyformat.info

strip-gotcha

When working with strip, lstrip, or rstrip, you might think that this would be the case:

>>> "Monty Python".rstrip(" Python")
"Monty"
While this seems intuitive, it would actually result in:
"M"
as Python interprets the argument to these functions as a set of characters rather than a substring.

If you want to remove a prefix/suffix from a string, str.removeprefix and str.removesuffix are recommended and were added in 3.9.

>>> "Monty Python".removesuffix(" Python")
"Monty"
See the documentation of str.removeprefix and str.removesuffix for more information.

system-python

Why Avoid System Python for Development on Unix-like Systems:

  • Critical Operating System Dependencies: Altering the system Python installation may harm internal operating system dependencies.
  • Stability and Security Concerns: System interpreters lag behind current releases, lacking the latest features and security patches.
  • Limited Package Control: External package management restricts control over versions, leading to compatibility issues with outdated packages.

Recommended Approach:

  • Install Independent Interpreter: Install Python from source or utilize a virtual environment for flexibility and control.
  • Utilize Pyenv or Similar Tools: Manage multiple Python versions and create isolated development environments for smoother workflows.
tools

The Tools page on our website contains a couple of the most popular tools for programming in Python.

traceback

Please provide the full traceback for your exception in order to help us identify your issue. While the last line of the error message tells us what kind of error you got, the full traceback will tell us which line, and other critical information to solve your problem. Please avoid screenshots so we can copy and paste parts of the message.

A full traceback could look like:

Traceback (most recent call last):
  File "my_file.py", line 5, in <module>
    add_three("6")
  File "my_file.py", line 2, in add_three
    a = num + 3
        ~~~~^~~
TypeError: can only concatenate str (not "int") to str
If the traceback is long, use our pastebin.

type-hint

A type hint indicates what type a variable is expected to be.

def add(a: int, b: int) -> int:
    return a + b
The type hints indicate that for our add function the parameters a and b should be integers, and the function should return an integer when called.

It's important to note these are just hints and are not enforced at runtime.

add("hello ", "world")
The above code won't error even though it doesn't follow the function's type hints; the two strings will be concatenated as normal.

Third party tools like mypy can validate your code to ensure it is type hinted correctly. This can help you identify potentially buggy code, for example it would error on the second example as our add function is not intended to concatenate strings.

mypy's documentation contains useful information on type hinting, and for more information check out this documentation page.

underscore
  • __name__: Used to implement special behaviour, such as the + operator for classes with the __add__ method. More info
  • _name: Indicates that a variable is "private" and should only be used by the class or module that defines it
  • name_: Used to avoid naming conflicts. For example, as class is a keyword, you could call a variable class_ instead
  • __name: Causes the name to be "mangled" if defined inside a class. More info

A single underscore, _, has multiple uses: - To indicate an unused variable, e.g. in a for loop if you don't care which iteration you are on

for _ in range(10):
    print("Hello World")
- In the REPL, where the previous result is assigned to the variable _
>>> 1 + 1  # Evaluated and stored in `_`
    2
>>> _ + 3  # Take the previous result and add 3
    5
- In integer literals, e.g. x = 1_500_000 can be written instead of x = 1500000 to improve readability

See also "Reserved classes of identifiers" in the Python docs, and this more detailed guide.

venv

Virtual environments are isolated Python environments, which make it easier to keep your system clean and manage dependencies. By default, when activated, only libraries and scripts installed in the virtual environment are accessible, preventing cross-project dependency conflicts, and allowing easy isolation of requirements.

To create a new virtual environment, you can use the standard library venv module: python3 -m venv .venv (replace python3 with python or py on Windows)

Then, to activate the new virtual environment:

Windows (PowerShell): .venv\Scripts\Activate.ps1 or (Command Prompt): .venv\Scripts\activate.bat MacOS / Linux (Bash): source .venv/bin/activate

Packages can then be installed to the virtual environment using pip, as normal.

For more information, take a read of the documentation. If you run code through your editor, check its documentation on how to make it use your virtual environment. For example, see the VSCode or PyCharm docs.

Tools such as poetry and pipenv can manage the creation of virtual environments as well as project dependencies, making packaging and installing your project easier.

Note: When using PowerShell in Windows, you may need to change the execution policy first. This is only required once per user:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

voice-verification

Can’t talk in voice chat? Check out <#764802555427029012> to get access. The criteria for verifying are specified there.

windows-path

If you have installed Python but forgot to check the Add Python to PATH option during the installation, you may still be able to access your installation with ease.

If you did not uncheck the option to install the py launcher, then you'll instead have a py command which can be used in the same way. If you want to be able to access your Python installation via the python command, then your best option is to re-install Python (remembering to tick the Add Python to PATH checkbox).

You can pass any options to the Python interpreter, e.g. to install the numpy module from PyPI you can run py -3 -m pip install numpy or python -m pip install numpy.

You can also access different versions of Python using the version flag of the py command, like so:

C:\Users\Username> py -3.7
... Python 3.7 starts ...
C:\Users\Username> py -3.6
... Python 3.6 starts ...
C:\Users\Username> py -2
... Python 2 (any version installed) starts ...

with

The with keyword triggers a context manager. Context managers automatically set up and take down data connections, or any other kind of object that implements the magic methods __enter__ and __exit__.

with open("test.txt", "r") as file:
    do_things(file)
The above code automatically closes file when the with block exits, so you never have to manually do a file.close(). Most connection types, including file readers and database connections, support this.

For more information, read the official docs, watch Corey Schafer\'s context manager video, or see PEP 343.

xy-problem

The XY problem can be summarised as asking about your attempted solution, rather than your actual problem.

Often programmers will get distracted with a potential solution they've come up with, and will try asking for help getting it to work. However, it's possible this solution either wouldn't work as they expect, or there's a much better solution instead.

For more information and examples, see http://xyproblem.info/.

ytdl

Per Python Discord's Rule 5, we are unable to assist with questions related to youtube-dl, pytube, or other YouTube video downloaders, as their usage violates YouTube's Terms of Service.

For reference, this usage is covered by the following clauses in YouTube's TOS, as of 2021-03-17:

The following restrictions apply to your use of the Service. You are not allowed to:

1. access, reproduce, download, distribute, transmit, broadcast, display, sell, license, alter, modify or otherwise use any part of the Service or any Content except: (a) as specifically permitted by the Service;  (b) with prior written permission from YouTube and, if applicable, the respective rights holders; or (c) as permitted by applicable law;

3. access the Service using any automated means (such as robots, botnets or scrapers) except: (a) in the case of public search engines, in accordance with YouTube’s robots.txt file; (b) with YouTube’s prior written permission; or (c) as permitted by applicable law;

9. use the Service to view or listen to Content other than for personal, non-commercial use (for example, you may not publicly screen videos or stream music from the Service)

zip

The zip function allows you to iterate through multiple iterables simultaneously. It joins the iterables together, almost like a zipper, so that each new element is a tuple with one element from each iterable.

letters = 'abc'
numbers = [1, 2, 3]
# list(zip(letters, numbers)) --> [('a', 1), ('b', 2), ('c', 3)]
for letter, number in zip(letters, numbers):
    print(letter, number)
The zip() iterator is exhausted after the length of the shortest iterable is exceeded. If you would like to retain the other values, consider using itertools.zip_longest.

For more information on zip, please refer to the official documentation.