Planet Python
Last update: January 19, 2020 10:48 AM UTC
January 19, 2020
Python Circle
How to set a variable in Django template
Declaring a new variable in Django template, Set the value of a variable in Django template, using custom template tag in Django, Defining variables in Django template tag
Encryption-Decryption in Python Django
How to encrypt and decrypt the content in Django, Encrypting the critical information in Django App, Encrypting username, email and password in Django, Django security
Top 5 Python Books
top 10 python programming books, List of python books to start with, Start with a python book collection, Buy best python books, Top and best python books,
Codementor
Why ASGI is Replacing WSGI in Django
Talks about why ASGI is replacing WSGI for Django development and the future it holds for Django development moving forward.
January 18, 2020
Python Circle
How to display flash messages in Django templates
flash messages in Django template, one-time notifications in Django template, messages framework Django, displaying success message in Django, error message display in Django
Catalin George Festila
Python 3.7.5 : Django security issues - part 001.
Django like any website development and framework implementation requires security settings and configurations. Today I will present some aspects of this topic and then I will come back with other information. 1. First, check your security vulnerabilities by the following command: [mythcat@desk django]$ source env/bin/activate (env) [mythcat@desk django]$ cd mysite (env) [mythcat@desk mysite]$
Python 3.7.5 : Is Django the best web framework?
This is the question for today in order to lineup the Django features with any web framework from my point of view. Let's start with a brief introduction to this framework: Django was created in the fall of 2003, when the web programmers at the Lawrence Journal-World newspaper, Adrian Holovaty and Simon Willison, began using Python to build applications. Jacob Kaplan-Moss was hired early in
IslandT
Create the input text box with tkinter
In the previous post, I have written a python program to create the database, earning table as well as input the first row of data into the earning table. In this chapter, I will create a simple UI to accept the user’s input so we do not need to hardcoded the values into the SQL query. I will leave the SQL commit code to the next chapter, we will only create a simple input’s UI in this chapter first.
A description box and the earning box of the Earning Input user interface
As you can see I will create the above simple UI with tkinter which can then be further upgraded in the future to include more stuff.
import tkinter as tk
from tkinter import ttk
win = tk.Tk()
win.title("Earning Input")
def submit():
pass
#create label frame for ui
earn= ttk.Labelframe(win, text = "Daily Earning Input")
earn.grid(column=0, row=0, padx=4, pady=4)
# create label for description
dLabel = ttk.Label(earn, text="Description:").grid(column=0, row=0)
# create text box for description
description = tk.StringVar()
descriptionEntry = ttk.Entry(earn, width=13, textvariable=description)
descriptionEntry.grid(column=1, row=0)
# create label for earning
eLabel = ttk.Label(earn, text="Earning:").grid(column=2, row=0)
# create text box for earning
earning = tk.StringVar()
earningEntry = ttk.Entry(earn, width=13, textvariable=earning)
earningEntry.grid(column=3, row=0)
# create the action button
action = ttk.Button(earn, text="submit", command=submit)
action.grid(column=5, row=0)
win.resizable(0,0)
win.mainloop()
I will write a program to submit the above data to the earning table in the next chapter.
This website will update constantly because I have decided to make writing articles one of my income streams, so do subscribe to the RSS feed of this site, and also, do read the other topic besides the python related one if you love another topic as well!
This is just for your info, I have found out another free to use universal database browser which you can download and play around through this link! If you are a software developer and would like me to include one of your free product in my post that is related to your type of product, do leave your comment under that particular post, I would really like to help a developer likes you to promote your software, so drop me a suggestion!
Peter Bengtsson
JavaScript destructuring like Python kwargs with defaults
In Python
I'm sure it's been blogged about a buncha times before but, I couldn't find it, and I had to search too hard to find an example of this. Basically, what I'm trying to do is what Python does in this case, but in JavaScript:
def do_something(arg="notset", **kwargs):
print(f"arg='{arg.upper()}'")
do_something(arg="peter")
do_something(something="else")
do_something()
In Python, the output of all this is:
arg='PETER' arg='NOTSET' arg='NOTSET'
It could also have been implemented in a more verbose way:
def do_something(**kwargs):
arg = kwargs.get("arg", "notset")
print(f"arg='{arg.upper()}'")
This more verbose format has the disadvantage that you can't quickly skim it and see and what the default is. That thing (arg = kwargs.get("arg", "notset")) might happen far away deeper in the function, making it hard work to spot the default.
In JavaScript
Here's the equivalent in JavaScript (ES6?):
function doSomething({ arg = "notset", ...kwargs } = {}) {
return `arg='${arg.toUpperCase()}'`;
}
console.log(doSomething({ arg: "peter" }));
console.log(doSomething({ something: "else" }));
console.log(doSomething());
Same output as in Python:
arg='PETER' arg='NOTSET' arg='NOTSET'
Notes
I'm still not convinced I like this syntax. It feels a bit too "hip" and too one-liner'y. But it's also pretty useful.
Mind you, the examples here are contrived because they're so short in terms of the number of arguments used in the function.
A more realistic thing like be a function that lists, upfront, all the possible parameters and for some of them, it wants to point out some defaults. E.g.
function processFolder({
source,
destination = "/tmp",
quiet = false,
verbose = false
} = {}) {
console.log({ source, destination, quiet, verbose });
// outputs
// { source: '/user', destination: '/tmp', quiet: true, verbose: false }
}
console.log(processFolder({ source: "/user", quiet: true }));
One could maybe argue that arguments that don't have a default are expected to always be supplied so they can be regular arguments like:
function processFolder(source, {
destination = "/tmp",
quiet = false,
verbose = false
} = {}) {
console.log({ source, destination, quiet, verbose });
// outputs
// { source: '/user', destination: '/tmp', quiet: true, verbose: false }
}
console.log(processFolder("/user", { quiet: true }));
But, I quite like keeping all arguments in an object. It makes it easier to write wrapper functions and I find this:
setProfile(
"My biography here",
false,
193.5,
230,
["anders", "bengt"],
"South Carolina"
);
...harder to read than...
setProfile({
bio: "My biography here",
dead: false,
height: 193.5,
weight: 230,
middlenames: ["anders", "bengt"],
state: "South Carolina"
});
January 17, 2020
PyCon
The 9th Annual PyLadies Auction
The PyLadies Auction is returning to PyCon 2020!
If you haven't attended one previously, you're missing out! There's charity. There's competition. There's laughter, food, and drinks. There are auction paddles flying in the air as people graciously give money where it has impact.
The PyLadies auction holds a special place in my heart – I attended my first PyCon in 2015 thanks to financial aid from PyLadies. If you haven't heard of PyLadies before, we are an international mentorship group with a focus on helping more women become active participants and leaders in the Python open-source community. Last year, the auction raised over $44,000 from the 58 items auctioned off.
For a glimpse into what a night at the PyLadies Auction is like, check out Lacey Williams Henschel’s live-tweet thread from last year.
The Details
- Date & Time: Saturday, April 18, 2020 at 6:30pm
- Location: Westin Hotel, 1000 Penn Ave, Pittsburgh, PA (This hotel is connected to the convention center via a walkway)
- Cost: Admission to the auction is $5 USD and includes light food and 1 free drink ticket and a cash bar. Tickets are available as part of your PyCon registration or can be purchased at the door, but keep in mind that tickets have sold out in previous years!
- Structure: A mix of live-bidding and raffle prizes, though neither are required to attend
Donations
It wouldn't be much of an auction without items to win! If you're interested in donating an item, you can email pycon-auction@python.org or fill out this form.<3
Support for the PyLadies auction comes from the PSF and our sponsors who help cover the cost of the venue, food, and drinks.For more information, check out the official event information and be sure to find a PyLadies event near you.
I hope to see you there!
Tim Arnold / reachtim
Reading Binary Data with Python
Overview
When you deal with external binary data in Python, there are a couple of ways to get that
data into a data structure. You can use the ctypes module to define the data structure
or you can use the struct python module.
You will see both methods used when you explore tool repositories on the web. This article shows you how to use each one to read an IPv4 header off the network. It’s up to you to decide which method you prefer; either way will work fine.
ctypesis a foreign function library for Python. It deals with C-based languages to provide C-compatible data types, and enables you to call functions in shared libraries.structconverts between Python values and C structs that are represented as Python bytes objects.
So ctypes handles binary data types in addition to a lot of other functionality, while
handling binary data is the main purpose of the struct module.
Let’s see how these two libraries are used when we need to decode an IPv4 header off the network.
First, here’s the structure of the IPv4 header. This is from the IETF RFC 791:
A summary of the contents of the internet header follows:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|Version| IHL |Type of Service| Total Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identification |Flags| Fragment Offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Time to Live | Protocol | Header Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Destination Address |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Example Internet Datagram Header
Initial Data from the Network
We need some data to work with, so let’s get a single packet from the network. This little snippet show do fine. I ran this on Linux.
import socket
import sys
def sniff(host):
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_ICMP)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# read and return a single packet
return sniffer.recvfrom(65535)
if __name__ == '__main__':
if len(sys.argv) == 2:
host = sys.argv[1]
else:
host = '192.168.1.69'
buff = sniff(host)
We just grab a single raw packet from the network and put it into a variable, buff.
So now that we have binary data, let’s look at how to use it.
ctypes module
The following code snippet defines a new class, IP that
can read a packet and parse the header into its separate fields.
from ctypes import *
import socket
import struct
class IP(Structure):
_fields_ = [
("ihl", c_ubyte, 4),
("version", c_ubyte, 4),
("tos", c_ubyte, 8),
("len", c_ushort, 16),
("id", c_ushort, 16),
("offset", c_ushort, 16),
("ttl", c_ubyte, 8),
("protocol_num", c_ubyte, 8),
("sum", c_ushort, 16),
("src", c_uint32, 32),
("dst", c_uint32, 32)
]
def __new__(cls, socket_buffer=None):
return cls.from_buffer_copy(socket_buffer)
def __init__(self, socket_buffer=None):
# human readable IP addresses
self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
You can see that the _fields_ structure defines each part of the header,
giving the width in bits as the last argument. Being able to specify the bit
width is handy. Our IP class inherits
from the ctypes Structure class, which specifies that we must have
a defined _fields_ structure before any instance is created.
Class Instantiation
The wrinkle with ctypes Structure abstract base class is the __new__ method.
See the documentation for full details:
ctypes module.
The __new__ method takes the class reference as the first argument. It creates and
returns an instance of the class, which passes to the __init__ method.
We create the instance normally, but underneath, Python invokes the class
method __new__, which fills out the _fields_ data structure immediately before
instantiation (when the __init__ method is called). As long as
you’ve defined the structure beforehand, just pass the __new__ method the
external (network packet) data, and the fields magically appear as attributes
on your instance.
struct module
The struct module provides format characters that you used to specify the structure
of the binary data. The first character (in our case, <) specifies the “endianness” of the
data. See the documentation for full details:
struct module.
import ipaddress
import struct
class IP:
def __init__(self, buff=None):
header = struct.unpack('<BBHHHBBH4s4s', buff)
self.ver = header[0] >> 4
self.ihl = header[0] & 0xF
self.tos = header[1]
self.len = header[2]
self.id = header[3]
self.offset = header[4]
self.ttl = header[5]
self.protocol_num = header[6]
self.sum = header[7]
self.src = header[8]
self.dst = header[9]
# human readable IP addresses
self.src_address = ipaddress.ip_address(self.src)
self.dst_address = ipaddress.ip_address(self.dst)
# map protocol constants to their names
self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}
Here are the individual parts of the header.
- B 1 byte (
ver,hdrlen) - B 1 byte
tos - H 2 bytes
total len - H 2 bytes
identification - H 2 bytes
flags + frag offset - B 1 byte
ttl - B 1 byte
protocol - H 2 bytes
checksum - 4s 4 bytes
src ip - 4s 4 bytes
dst ip
Everything is pretty straightforward, but with ctypes, we could specify the bit-width
of the individual pieces. With struct, there’s no format character for a nybble (4 bits),
so we have to do some manipulation to get the ver and hdrlen from the first part of
the header.
Binary Manipulations
The wrinkle with struct in this example is that we need to do some manipulation
of header[0], which contains a single byte but we need to create two variables
from that byte, each containing a nybble.
High nybble
We have one byte and for the ver variable, we want the high-order nybble.
The typical way you get the
high nybble of a byte is to right-shift.
We right shift the byte by 4 places, which is like prepending 4 zeros
at the front so the last 4 bytes fall off, leaving us with the first nybble:
0 1 0 1 0 1 1 0 >> 4
-----------------------------
0 0 0 0 0 1 0 1
Low nybble
We have one byte and for the hdrlen variable, we want the low-order nybble.
The typical way you get the
low nybble of a byte is to AND it with F (00001111):
0 1 0 1 0 1 1 0 &F
0 0 0 0 1 1 1 1
-----------------------------
0 0 0 0 0 1 1 0
Let’s look an example in the Python REPL:
>>> m = 66
>>> m
66
>>> bin(m)
'0b1000010' # or 0100 0010
>>> bin(m>>4)
'0b100' # or 0100
>>> bin(m&0xF)
'0b10' # or 0010
Now, more specifically to our IPv4 case, the first byte in the header is
always 0x45 = 69 decimal = 01000101 binary.
See what that looks like when we right-shift
it by 4 and then AND it with F:
>>> '{0:08b}'.format(0x45)
'01000101'
>>> '{0:04b}'.format(0x45>>4)
'0100'
>>> '{0:04b}'.format(0x45&0xF)
'0101'
You don’t have to know binary manipulation backward and forward for decoding an IP header,
but there are some patterns like these (shift and AND) you will see over and over again as you code and as you explore other hackers’ code.
That seems like a lot of work doesn’t it? In the case where we have to do some bit
shifting, it does take effort. But for many cases (e.g. ICMP),
everything works on an 8-byte boundary and so is very
simple to set up. Here is an “Echo Reply” ICMP message;
you can see that each parameter of the ICMP header can be
defined in a struct with one of the existing format letters (BBHHH) (RFC777):
Echo or Echo Reply Message
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
A quick way to parse that would simply be:
class ICMP:
def __init__(self, buff):
header = struct.unpack('<BBHHH', buff)
self.type = header[0]
self.code = header[1]
self.sum = header[2]
self.id = header[3]
self.seq = header[4]
Conclusion
You can use either the ctypes module or the struct module to read and parse
binary data. Here is an example of instantiating the class no matter which method
you use. You instantiate the IP class with your packet data
in the variable buff:
mypacket = IP(buff)
print(f'{mypacket.src_address} -> {mypacket.dst_address}')
With ctypes, make sure you define your _fields_ structure and hand
the data to it in the _new_ method. When you instantiate the class, you’ll have the
access to the data attributes automatically.
With struct, you define how to read the data with a format string. For data attributes
that don’t lie on the 8-byte boundary, you may need to do some binary manipulation.
In short, use whichever method fits your brain. But always be aware that you may see code from others that use a different method. Hopefully, now you’ll see it and understand it.
Codementor
How to Build RESTful APIs with Python and Flask
For some time now I have been working with Python but I just got to try out Flask recently, so I felt it would be nice to write about it. In this aritcle I'll discuss about Flask and how you can...
Python Software Foundation
Start using 2FA and API tokens on PyPI
To increase the security of PyPI downloads, we have added two-factor authentication (2FA) as a login security option, and API tokens for uploading packages. This is thanks to a grant from the Open Technology Fund, coordinated by the Packaging Working Group of the Python Software Foundation.
If you maintain or own a project on the Python Package Index, you should start using these features. Click "help" on PyPI for instructions. (These features are also available on Test PyPI.)
Details and plans for the future:
2FA:
Two-factor authentication (2FA) makes your account more secure by requiring two things in order to log in: something you know and something you own.
In PyPI's case, "something you know" is your username and password, while "something you own" can be an application to generate a temporary code, or a security device (most commonly a USB key).
Why? This will help improve the security of your PyPI user accounts, and thus
reduce the risk of vandals, spammers, and thieves gaining account
access. Protecting login via the website safeguards against malicious changes to
project ownership, deletion of old releases, and account takeovers.
PyPI's implementation of the WebAuthn standard and the TOTP standard mean you can use any TOTP authentication application and/or any 2FA device that meets the FIDO standard. (We launched WebAuthn support last year; this week it comes out of beta.)
Go to your account settings to add a second factor.
![]() |
| Add a second factor in your account settings. |
![]() |
| Create a key name in the PyPI interface. |
API tokens:
![]() |
| In your Account Settings, select "Add API token". |
![]() |
| PyPI interface for adding an API token for package upload. |
![]() |
| Immediately after creating the API token, PyPI gives the user one chance to copy it. |
You can create a token for an entire PyPI user account, in which case, the token will work for all projects associated with that account. Alternatively, you can limit a token's scope to a specific project. That way, if a token is compromised, you can just revoke and recreate that token, instead of having to change your password in lots of automated processes.
![]() |
| PyPI token management interface |
Go to your account settings to add an API token.
Future:
In the future, PyPI will set and enforce a policy requiring users with two-factor authentication enabled to use API tokens to upload (rather than just their password, without a second factor). We do not yet know when we will make this policy change. When we do, we'll announce it.
Thanks:
Thanks to the Open Technology Fund for funding this work.
More donor-funded work is in progress on pip and PyPI, via the PSF's Packaging Working Group. Please sign up for the PyPI Announcement Mailing List for future updates.
PyPy Development
Leysin Winter sprint 2020: Feb 28 - March 7th
The next PyPy sprint will be in Leysin, Switzerland, for the fourteenth
time. This is a fully public sprint: newcomers and topics other than
those proposed below are welcome.
Goals and topics of the sprint
The list of topics is open. For reference, we would like to work at least partially on the following topics:- HPy
- Python 3.7 support (buildbot status)
Times and accomodation
The sprint will occur for one week starting on Friday, the 28th of February, to Friday or Saturday, the 6th or 7th of March 2020. It will occur in Les Airelles, a different bed-and-breakfast place from the traditional one in Leysin. It is a nice old house at the top of the village.We have a 4- or 5-people room as well as up to three double-rooms. Please register early! These rooms are not booked for the sprint in advance, and might be already taken if you end up announcing yourself late. (But it is of course always possible to book at a different place in Leysin.)
For more information, see our repository or write to me directly at armin.rigo@gmail.com.
January 16, 2020
py.CheckIO
Mocking in Python
Stack Abuse
Variable-Length Arguments in Python with *args and **kwargs
Introduction
Some functions have no arguments, others have multiple. There are times we have functions with arguments we don't know about beforehand. We may have a variable number of arguments because we want to offer a flexible API to other developers or we don't know the input size. With Python, we can create functions to accept any amount of arguments.
In this article, we will look at how we can define and use functions with variable length arguments. These functions can accept an unknown amount of input, either as consecutive entries or named arguments.
Using Many Arguments with *args
Let's implement a function that finds the minimum value between two numbers. It would look like this:
def my_min(num1, num2):
if num1 < num2:
return num1
return num2
my_min(23, 50)
23
It simply checks if the first number is smaller than the second number. If it is, then the first number is returned. Otherwise, the second number is returned.
If we would like to find a minimum of 3 numbers, we can add another argument to my_min() and more if-statements. If our minimum function needs to find the lowest number of any indeterminate amount, we can use a list:
def my_min(nums):
result = nums[0]
for num in nums:
if num < result:
result = num
return result
my_min(4, 5, 6, 7, 2)
2
While this works, our coders now have to enclose their numbers in a list, which isn't as straightforward as it was when we had two or three defined arguments. Let's get the best of both worlds with variable length arguments.
Variable-length arguments, varargs for short, are arguments that can take an unspecified amount of input. When these are used, the programmer does not need to wrap the data in a list or an alternative sequence.
In Python, varargs are defined using the *args syntax. Let's reimplement our my_min() function with *args:
def my_min(*args):
result = args[0]
for num in args:
if num < result:
result = num
return result
my_min(4, 5, 6, 7, 2)
2
Note:
argsis just a name, you can name that vararg anything as long as it is preceded by a single asterisk (*). It's best practice to keep naming itargsto make it immediately recognizable.
Any argument that comes after *args must be a named argument - an argument that's referenced by its name instead of its position. With *args you can no longer reference another argument by its position.
Also, you can only have on *args type vararg in a function.
You may be thinking that the solution with *args is very similar to the list solution. That's because *args is internally a Tuple, which is an iterable sequence similar to lists. If you'd like to verify it's type, you can enter the code in your Python interpreter:
$ python3
>>> def arg_type_test(*args):
... print(type(args))
...
>>> arg_type_test(1, 2)
<class 'tuple'>
With *args, we can accept multiple arguments in sequence as is done in my_min(). These arguments are processed by their position. What if we wanted to take multiple arguments, but reference them by their name? We'll take a look at how to do this in the next section.
Using Many Named Arguments with **kwargs
Python can accept multiple keyword arguments, better known as **kwargs. It behaves similarly to *args, but stores the arguments in a dictionary instead of tuples:
def kwarg_type_test(**kwargs):
print(kwargs)
kwarg_type_test(a="hi")
kwarg_type_test(roses="red", violets="blue")
The output will be:
{'a': 'hi'}
{'roses': 'red', 'violets': 'blue'}
By using a dictionary, **kwargs can preserve the names of the arguments, but it would not be able to keep their position.
Note: Like
args, you can use any other name thankwargs. However, best practice dictates that you should consistently usekwargs.
Since **kwargs is a dictionary, you can iterate over them like any other using the .items() method:
def kwargs_iterate(**kwargs):
for i, k in kwargs.items():
print(i, '=', k)
kwargs_iterate(hello='world')
When run, our console will show:
hello = world
Keyword arguments are useful when you aren't sure if an argument is going to be available. For example, if we had a function to save a blog post to a database, we would save the information like the content and the author. A blog post may have tags and categories, though those aren't always set.
We can define a function like this:
def save_blog_post(content, author, tags=[], categories=[]):
pass
Alternatively, we allow the function caller to pass any amount of arguments, and only associate tags and categories if they're set:
def save_blog_post(content, author, **kwargs):
if kwargs.get('tags'):
# Save tags with post
pass
if kwargs.get('categories'):
# Save categories with post
pass
Now that we have a grasp of both types of support for variable length arguments, let's see how we can combine the two in one function.
Combining Varargs and Keyword Arguments
Quite often we want to use both *args and **kwargs together, especially when writing Python libraries or reusable code. Lucky for us, *args and **kwargs play nicely together, and we can use them in the following way:
def combined_varargs(*args, **kwargs):
print(args)
print(kwargs)
combined_varargs(1, 2, 3, a="hi")
If you run that code snippet you'll see:
(1, 2, 3)
{'a': 'hi'}
When mixing the positional and named arguments, positional arguments must come before named arguments. Furthermore, arguments of a fixed length come before arguments with variable length. Therefore, we get an order like this:
- Known positional arguments
*args- Known named arguments
**kwargs
A function with all types of arguments can look like this:
def super_function(num1, num2, *args, callback=None, messages=[], **kwargs):
pass
Once we follow that order when defining and calling functions with varargs and keyword arguments, we'll get the behavior we expect from them.
So far we've used the *args and **kwargs syntax for function definitions. Python allows us to use the same syntax when we call functions as well. Let's see how!
Unpacking Arguments with *args and **kwargs
Let's consider a function add3(), that accepts 3 numbers and prints their sum. We can create it like this:
def add3(num1, num2, num3):
print("The grand total is", num1 + num2 + num3)
If you had a list of numbers, you can use this function by specifying which list item is used as an argument:
magic_nums = [32, 1, 7]
add3(magic_nums[0], magic_nums[1], magic_nums[2])
If your run this code, you will see:
The grand total is 40
While this works, we can make this more succinct with *args syntax:
add3(*magic_nums)
The output is The grand total is 40, just like before.
When we use *args syntax in a function call, we are unpacking the variable. By unpacking, we mean that we are pulling out the individual values of the list. In this case, we pull out each element of the list and place them in the arguments, where position 0 corresponds to the first argument.
You can also similarly unpack a tuple:
tuple_nums = (32, 1, 7)
add3(*tuple_nums) # The grand total is 40
If you would like the unpack a dictionary, you must use the **kwargs syntax.
dict_nums = {
'num1': 32,
'num2': 1,
'num3': 7,
}
add3(**dict_nums) # The grand total is 40
In this case, Python matches the dictionary key with the argument name and sets its value.
And that's it! You can easier manage your function calls by unpacking values instead of specifying each argument that needs a value from an object.
Conclusion
With Python, we can use the *args or **kwargs syntax to capture a variable number of arguments in our functions. Using *args, we can process an indefinite number of arguments in a function's position. With **kwargs, we can retrieve an indefinite number of arguments by their name.
While a function can only have one argument of variable length of each type, we can combine both types of functions in one argument. If we do, we must ensure that positional arguments come before named arguments and that fixed arguments come before those of variable length.
Python allows us to use the syntax for function calls as well. If we have a list or a tuple and use the *args syntax, it will unpack each value as positional arguments. If we have a dictionary and use **kwargs syntax, then it will match the names of the dictionary keys with the names of the function arguments.
Are you working on a function that can benefit from these type of arguments? Or maybe you can refactor a function and make it future proof? Let us know what you're working on!
Codementor
Build REST API with Flask & SQLAlchemy
Flask is a great framework that enables you to build web applications quickly with Python. It's fast, small, and fun to work with. In this…
Talk Python to Me
#247 Solo maintainer of open-source in academia
Do you run an open-source project? Does it seem like you never have enough time to support it? Have you considered starting one but are unsure you can commit to it? It's a real challenge.
Python Bytes
#164 Use type hints to build your next CLI app
Programiz
Python pip
In this tutorial, we will learn how to use pip to install and manage Python packages.
Codementor
Reuven Lerner made one of the first 100 websites... ever... and other things I learned recording his DevJourney
Reuven Lerner is a consultant, an author and a teacher. After interviewing him for the DevJourney podcast, here are the key takeways I personally took out of the discussion.
IslandT
Create a daily earning database with Python SQLite
- Download DB Browser to view the earning table
- Create earning table
- Insert very first data
In this chapter, we will start a project which will then record my daily earning in the future. We will create the earing table and populate the first row of data into that table. I can then view my earning table using DB Browser which is a browser uses to create, edit, plot and view the SQLite table’s items.
First of all, let us go to the homepage of DB Browser to download DB Browser through this link. I will temporarily use this tool to view my SQLite table but my final objective is to create my own SQLIte table viewer using the tkinter module. I will go phase by phase to accomplish my objective.
import sqlite3
conn = sqlite3.connect('daily_earning.db')
print ("Opened earning database successfully")
conn.execute('''CREATE TABLE DAILY_EARNING_CHART
(ID INTEGER PRIMARY KEY AUTOINCREMENT,
DESCRIPTION TEXT (50) NOT NULL,
EARNING DOUBLE NOT NULL,
TIME TEXT NOT NULL);''')
print ("Table created successfully")
conn.close()
The next step is to create the SQLite table with a description of the item and the earning I have made every day!
The daily_earning.db file will be the database that we will keep in our computer, the table has been created in that database.
Finally, we will insert some data into the table above.
import sqlite3
try:
sqliteConnection = sqlite3.connect('daily_earning.db')
cursor = sqliteConnection.cursor()
print("Successfully Connected to SQLite")
sqlite_insert_query = """INSERT INTO DAILY_EARNING_CHART
(DESCRIPTION,EARNING,TIME)
VALUES
('Selling Shoe',33.90, datetime('now', 'localtime'))"""
count = cursor.execute(sqlite_insert_query)
sqliteConnection.commit()
print("Record inserted successfully into DAILY_EARNING_CHART table", cursor.rowcount)
cursor.close()
except sqlite3.Error as error:
print("Failed to insert earning data into sqlite table", error)
finally:
if (sqliteConnection):
sqliteConnection.close()
print("The SQLite connection is closed")
As you can see, we have inserted the earning description and earning into the above table. We will use tkinter to create a user interface that will accept the data from the user’s keyboard in the future instead of hardcoded the description and the earning into the insert query!
Now let us look at what we have at the moment, I start the DB Browser program, then open the database file, File->Open Database, open the daily_earning.db file, select the table under the Database Structure tab and view the table under the Browse Data tab. Don’t forget to select the table you want under the drop-down selection box.
Select the database’s table
Browse the data from a particular table!DB Browser is a great program and if you have not installed the program just go ahead and do so, that is all for today.
I will start a few python projects at the same time, this is one of them, another one is the video editing project, if you have not subscribed to this python related topic yet, go ahead and subscribe this topic through the rss feed on the sidebar of this website.
January 15, 2020
Codementor
Airflow Case Study: ProofPort
A case study of using Apache Airflow to complete transform the data pipeline at a SaaS startup - in just a few months.
John Cook
More efficient way to sum a list comprehension
List comprehensions in Python let you create a list declaratively, much like the way you would describe the set in English. For example,
[x**2 for x in range(10)]
creates a list of the squares of the numbers 0 through 9.
If you wanted the sum of these numbers, you could of course say
sum( [x**2 for x in range(10)] )
but in this case the brackets are unnecessary. The code is easier to read and more efficient if you omit the brackets.
sum( x**2 for x in range(10) )
With the brackets, Python constructs the list first then sums the elements. Without the brackets, you have a generator expression. Python will sum the values as they’re generated, not saving all the values in memory. This makes no difference for a short list as above, but with a large list comprehension the generator expression could be more efficient.
Real Python
Arcade: A Primer on the Python Game Framework
Computer games are a great way to introduce people to coding and computer science. Since I was a player in my youth, the lure of writing video games was the reason I learned to code. Of course, when I learned Python, my first instinct was to write a Python game.
While Python makes learning to code more accessible for everyone, the choices for video game writing can be limited, especially if you want to write arcade games with great graphics and catchy sound effects. For many years, Python game programmers were limited to the pygame framework. Now, there’s another choice.
The arcade library is a modern Python framework for crafting games with compelling graphics and sound. Object-oriented and built for Python 3.6 and up, arcade provides the programmer with a modern set of tools for crafting great Python game experiences.
In this tutorial, you’ll learn how to:
- Install the
arcadelibrary - Draw items on the screen
- Work with the
arcadePython game loop - Manage on-screen graphic elements
- Handle user input
- Play sound effects and music
- Describe how Python game programming with
arcadediffers frompygame
This tutorial assumes you have an understanding of writing Python programs. Since arcade is an object-oriented library, you should also be familiar with object-oriented programming as well. All of the code, images, and sounds for this tutorial are available for download at the link below:
Download Assets: Click here to download the assets you'll use to make a game with arcade in this tutorial.
Background and Setup
The arcade library was written by Paul Vincent Craven, a computer science professor at Simpson College in Iowa, USA. As it’s built on top of the pyglet windowing and multimedia library, arcade features various improvements, modernizations, and enhancements over pygame:
- Boasts modern OpenGL graphics
- Supports Python 3 type hinting
- Has better support for animated sprites
- Incorporates consistent command, function, and parameter names
- Encourages separation of game logic from display code
- Requires less boilerplate code
- Maintains more documentation, including complete Python game examples
- Has a built-in physics engine for platform games
To install arcade and its dependencies, use the appropriate pip command:
$ python -m pip install arcade
On the Mac, you also need to install PyObjC:
$ python -m pip install PyObjC arcade
Complete installation instructions based on your platform are available for Windows, Mac, Linux, and even Raspberry Pi. You can even install arcade directly from source if you’d prefer.
Note: More recent versions of arcade utilize data classes, which are included in Python only for version 3.7 and later.
However, a backport is available on PyPI for Python 3.6 that you can install using pip:
$ python -m pip install dataclasses
See The Ultimate Guide to Data Classes in Python 3.7 for more information.
This tutorial assumes you’re using arcade 2.1 and Python 3.7 throughout.
Basic arcade Program
Before you dig in, let’s take a look at an arcade program that will open a window, fill it with white, and draw a blue circle in the middle:
1 # Basic arcade program
2 # Displays a white window with a blue circle in the middle
3
4 # Imports
5 import arcade
6
7 # Constants
8 SCREEN_WIDTH = 600
9 SCREEN_HEIGHT = 800
10 SCREEN_TITLE = "Welcome to Arcade"
11 RADIUS = 150
12
13 # Open the window
14 arcade.open_window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
15
16 # Set the background color
17 arcade.set_background_color(arcade.color.WHITE)
18
19 # Clear the screen and start drawing
20 arcade.start_render()
21
22 # Draw a blue circle
23 arcade.draw_circle_filled(
24 SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RADIUS, arcade.color.BLUE
25 )
26
27 # Finish drawing
28 arcade.finish_render()
29
30 # Display everything
31 arcade.run()
When you run this program, you’ll see a window that looks like this:
Let’s break this down line by line:
- Line 5 imports the
arcadelibrary. Without this, nothing else works. - Lines 8 to 11 define some constants you’ll use a little later, for clarity.
- Line 14 opens the main window. You provide the width, height, and title bar text, and
arcadedoes the rest. - Line 17 sets the background color using a constant from the
arcade.colorpackage. You can also specify an RGB color using a list or tuple. - Line 20 sets
arcadeinto drawing mode. Anything you draw after this line will be shown on the screen. - Lines 23 to 25 draw the circle by providing the center X and Y coordinates, the radius, and the color to use.
- Line 28 ends drawing mode.
- Line 31 displays your window for you to see.
If you’re familiar with pygame, then you’ll notice a few things are different:
- There is no
pygame.init(). All initialization is handled when you runimport arcade. - There is no explicitly-defined display loop. It’s handled in
arcade.run(). - There is no event loop here, either. Again,
arcade.run()handles events and provides some default behaviors, such as the ability to close the window. - You can use pre-defined colors for drawing rather than defining them all yourself.
- You have to start and finish drawing in arcade using
start_render()andfinish_render().
Let’s take a close look at the fundamental arcade concepts behind this program.
arcade Concepts
Like pygame, arcade code runs on almost every platform that supports Python. This requires arcade to deal with abstractions for various hardware differences on those platforms. Understanding these concepts and abstractions will help you design and develop your own games while understanding how arcade differs from pygame will help you adapt to its unique point of view.
Initialization
Since it deals with a variety of platforms, arcade must perform an initialization step before you can use it. This step is automatic and occurs whenever you import arcade, so there’s no additional code you need to write. When you import it, arcade does the following:
- Verify that you’re running on Python 3.6 or higher.
- Import the
pyglet_ffmeg2library for sound handling, if it’s available. - Import the
pygletlibrary for window and multimedia handling. - Set up constants for colors and key mappings.
- Import the remaining
arcadelibrary.
Contrast this with pygame, which requires a separate initialization step for each module.
Windows and Coordinates
Everything in arcade happens in a window, with you create using open_window(). Currently, arcade only supports a single display window. You can make the window resizable when you open it.
arcade uses the same Cartesian coordinate system you may have learned in algebra class. The window lives in quadrant I, with the origin point (0, 0) located in the lower-left corner of the screen. The x coordinate increases as you move right, and the y coordinate increases as you move up:
It’s important to note that this behavior is the opposite of pygame and many other Python game frameworks. It may take some time for you to adjust to this difference.
Drawing
Out of the box, arcade has functions for drawing various geometric shapes, including:
- Arcs
- Circles
- Ellipses
- Lines
- Parabolas
- Points
- Polygons
- Rectangles
- Triangles
All of the drawing functions begin with draw_ and follow a consistent naming and parameter pattern. There are different functions for drawing filled and outlined shapes:
Because rectangles are common, there are three separate functions for drawing them in different ways:
draw_rectangle()expects the x and y coordinates of the center of the rectangle, the width, and the height.draw_lrtb_rectangle()expects the left and right x coordinates, followed by the top and bottom y coordinates.draw_xywh_rectangle()uses the x and y coordinates of the bottom-left corner, followed by the width and the height.
Note that each function requires four parameters. You can also draw every shape using buffered drawing functions, which utilize vertex buffers to push everything directly to the graphics card for incredible performance improvements. All of the buffered drawing functions begin with create_ and follow consistent naming and parameter patterns.
Object-Oriented Design
At its core, arcade is an object-oriented library. Like pygame, you can write arcade code procedurally, as you did in the example above. However, the real power of arcade shows when you create completely object-oriented programs.
When you called arcade.open_window() in the example above, the code creates an arcade.Window object behind the scenes to manage that window. Later, you’ll create your own class based on arcade.Window to write a complete Python game.
First, take a look at the original example code, which now uses object-oriented concepts, to highlight the major differences:
1 # Basic arcade program using objects
2 # Displays a white window with a blue circle in the middle
3
4 # Imports
5 import arcade
6
7 # Constants
8 SCREEN_WIDTH = 600
9 SCREEN_HEIGHT = 800
10 SCREEN_TITLE = "Welcome to Arcade"
11 RADIUS = 150
12
13 # Classes
14 class Welcome(arcade.Window):
15 """Main welcome window
16 """
17 def __init__(self):
18 """Initialize the window
19 """
20
21 # Call the parent class constructor
22 super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
23
24 # Set the background window
25 arcade.set_background_color(arcade.color.WHITE)
26
27 def on_draw(self):
28 """Called whenever you need to draw your window
29 """
30
31 # Clear the screen and start drawing
32 arcade.start_render()
33
34 # Draw a blue circle
35 arcade.draw_circle_filled(
36 SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, RADIUS, arcade.color.BLUE
37 )
38
39 # Main code entry point
40 if __name__ == "__main__":
41 app = Welcome()
42 arcade.run()
Let’s take a look at this code line by line:
-
Lines 1 to 11 are the same as the earlier procedural example.
-
Line 15 is where the differences start. You define a class called
Welcomebased on the parent classarcade.Window. This allows you to override methods in the parent class as necessary. -
Lines 18 to 26 define the
.__init__()method. After calling the parent.__init__()method usingsuper()to set up the window, you set its background color, as you did before. -
Lines 28 to 38 define
.on_draw(). This is one of severalWindowmethods you can override to customize the behavior of yourarcadeprogram. This method is called every timearcadewants to draw on the window. It starts with a call toarcade.start_render(), followed by all your drawing code. You don’t need to callarcade.finish_render(), however, asarcadewill call that implicitly when.on_draw()ends. -
Lines 41 to 43 are your code’s main entry point. After you first create a new
Welcomeobject calledapp, you callarcade.run()to display the window.
This object-oriented example is the key to getting the most from arcade. One thing that you may have noticed was the description of .on_draw(). arcade will call this every time it wants to draw on the window. So, how does arcade know when to draw anything? Let’s take a look at the implications of this.
Game Loop
All of the action in pretty much every game occurs in a central game loop. You can even see examples of game loops in physical games like checkers, Old Maid, or baseball. The game loop begins after the game is set up and initialized, and it ends when the game does. Several things happen sequentially inside this loop. At a minimum, a game loop takes the following four actions:
- The program determines if the game is over. If so, then the loop ends.
- The user input is processed.
- The states of game objects are updated based on factors such as user input or time.
- The game displays visuals and plays sound effects based on the new state.
In pygame, you must set up and control this loop explicitly. In arcade, the Python game loop is provided for you, encapsulated in the arcade.run() call.
During the built-in game loop, arcade calls a set of Window methods to implement all of the functionality listed above. The names of these methods all begin with on_ and can be thought of as task or event handlers. When the arcade game loop needs to update the state of all Python game objects, it calls .on_update(). When it needs to check for mouse movement, it calls .on_mouse_motion().
By default, none of these methods do anything useful. When you create your own class based on arcade.Window, you override them as necessary to provide your own game functionality. Some of the methods provided include the following:
- Keyboard Input:
.on_key_press(),.on_key_release() - Mouse Input:
.on_mouse_press(),.on_mouse_release(),.on_mouse_motion() - Updating Game Object:
.on_update() - Drawing:
.on_draw()
You don’t need to override all of these methods, just the ones for which you want to provide different behavior. You also don’t need to worry about when they’re called, just what to do when they’re called. Next, you’ll explore how you can put all these concepts together to create a game.
Fundamentals of Python Game Design
Before you start writing any code, it’s always a good idea to have a design in place. Since you’ll be creating a Python game in this tutorial, you’ll design some gameplay for it as well:
- The game is a horizontally-scrolling enemy avoidance game.
- The player starts on the left side of the screen.
- The enemies enter at regular intervals and at random locations on the right.
- The enemies move left in a straight line until they are off the screen.
- The player can move left, right, up, or down to avoid the enemies.
- The player can’t move off the screen.
- The game ends when the player is hit by an enemy, or the user closes the window.
When he was describing software projects, a former colleague of mine once said, “You don’t know what you do until you know what you don’t do.” With that in mind, here are some things that you won’t cover in this tutorial:
- No multiple lives
- No scorekeeping
- No player attack capabilities
- No advancing levels
- No “Boss” characters
You’re free to try your hand at adding these and other features to your own program.
Imports and Constants
As with any arcade program, you’ll start by importing the library:
1 # Basic arcade shooter
2
3 # Imports
4 import arcade
5 import random
6
7 # Constants
8 SCREEN_WIDTH = 800
9 SCREEN_HEIGHT = 600
10 SCREEN_TITLE = "Arcade Space Shooter"
11 SCALING = 2.0
In addition to arcade, you also import random, as you’ll use random numbers later. The constants set up the window size and title, but what is SCALING? This constant is used to make the window, and the game objects in it, larger to compensate for high DPI screens. You’ll see it used in two places as the tutorial continues. You can change this value to suit the size of your screen.
Window Class
To take full advantage of the arcade Python game loop and event handlers, create a new class based on arcade.Window:
35 class SpaceShooter(arcade.Window):
36 """Space Shooter side scroller game
37 Player starts on the left, enemies appear on the right
38 Player can move anywhere, but not off screen
39 Enemies fly to the left at variable speed
40 Collisions end the game
41 """
42
43 def __init__(self, width, height, title):
44 """Initialize the game
45 """
46 super().__init__(width, height, title)
47
48 # Set up the empty sprite lists
49 self.enemies_list = arcade.SpriteList()
50 self.clouds_list = arcade.SpriteList()
51 self.all_sprites = arcade.SpriteList()
Your new class starts just like the object-oriented example above. On line 43, you define your constructor, which takes the width, height, and title of the game window, and use super() to pass those to the parent. Then you initialize some empty sprite lists on lines 49 through 51. In the next section, you’ll learn more about sprites and sprite lists.
Sprites and Sprite Lists
Your Python game design calls for a single player who starts on the left and can move freely around the window. It also calls for enemies (in other words, more than one) who appear randomly on the right and move to the left. While you could use the draw_ commands to draw the player and every enemy, it would quickly become difficult to keep it all straight.
Instead, most modern games use sprites to represent objects on the screen. Essentially, a sprite is a two-dimensional picture of a game object with a defined size that’s drawn at a specific position on the screen. In arcade, sprites are objects of class arcade.Sprite, and you’ll use them to represent your player as well as the enemies. You’ll even throw in some clouds to make the background more interesting.
Managing all these sprites can be a challenge. You’ll create a single-player sprite, but you’ll also be creating numerous enemies and cloud sprites. Keeping track of them all is a job for a sprite list. If you understand how Python lists work, then you’ve got the tools to use arcade’s sprite lists. Sprite lists do more than just hold onto all the sprites. They enable three important behaviors:
- You can update all the sprites in the list with a single call to
SpriteList.update(). - You can draw all the sprites in the list with a single call to
SpriteList.draw(). - You can check if a single sprite has collided with any sprite in the list.
You may wonder why you need three different sprite lists if you only need to manage multiple enemies and clouds. The reason is that each of the three different sprite lists exists because you use them for three different purposes:
- You use
.enemies_listto update the enemy positions and to check for collisions. - You use
.clouds_listto update the cloud positions. - Lastly, you use
.all_spritesto draw everything.
Now, a list is only as useful as the data it contains. Here’s how you populate your sprite lists:
53 def setup(self):
54 """Get the game ready to play
55 """
56
57 # Set the background color
58 arcade.set_background_color(arcade.color.SKY_BLUE)
59
60 # Set up the player
61 self.player = arcade.Sprite("images/jet.png", SCALING)
62 self.player.center_y = self.height / 2
63 self.player.left = 10
64 self.all_sprites.append(self.player)
You define .setup() to initialize the game to a known starting point. While you could do this in .__init__(), having a separate .setup() method is useful.
Imagine you want your Python game to have multiple levels, or your player to have multiple lives. Rather than restart the entire game by calling .__init__(), you call .setup() instead to reinitialize the game to a known starting point or set up a new level. Even though this Python game won’t have those features, setting up the structure makes it quicker to add them later.
After you set the background color on line 58, you then define the player sprite:
-
Line 61 creates a new
arcade.Spriteobject by specifying the image to display and the scaling factor. It’s a good idea to organize your images into a single sub-folder, especially on larger projects. -
Line 62 sets the y position of the sprite to half the height of the window.
-
Line 63 sets the x position of the sprite by placing the left edge a few pixels away from the window’s left edge.
-
Line 64 finally uses
.append()to add the sprite to the.all_spriteslist you’ll use for drawing.
Lines 62 and 63 show two different ways to position your sprite. Let’s take a closer look at all the sprite positioning options available.
Sprite Positioning
All sprites in arcade have a specific size and position in the window:
- The size, specified by
Sprite.widthandSprite.height, is determined by the graphic used when the sprite is created. - The position is initially set to have the center of the sprite, specified by
Sprite.center_xandSprite.center_y, at (0,0) in the window.
Once the .center_x and .center_y coordinates are known, arcade can use the size to calculate the Sprite.left, Sprite.right, Sprite.top, and Sprite.bottom edges as well.
This also works in reverse. For example, if you set Sprite.left to a given value, then arcade will recalculate the remaining position attributes as well. You can use any of them to locate the sprite or move it in the window. This is an extremely useful and powerful characteristic of arcade sprites. If you use them, then your Python game will require less code than pygame:
Now that you’ve defined the player sprite, you can work on the enemy sprites. The design calls for you to make enemy sprites appear at regular intervals. How can you do that?
Scheduling Functions
arcade.schedule() is designed exactly for this purpose. It takes two arguments:
- The name of the function to call
- The time interval to wait between each call, in seconds
Since you want both enemies and clouds to appear throughout the game, you set up one scheduled function to create new enemies, and a second to create new clouds. That code goes into .setup(). Here’s what that code looks like:
66 # Spawn a new enemy every 0.25 seconds
67 arcade.schedule(self.add_enemy, 0.25)
68
69 # Spawn a new cloud every second
70 arcade.schedule(self.add_cloud, 1.0)
Now all you have to do is define self.add_enemy() and self.add_cloud().
Adding Enemies
From your Python game design, enemies have three key properties:
- They appear at random locations on the right side of the window.
- They move left in a straight line.
- They disappear when they go off the screen.
The code to create an enemy sprite is very similar to the code to create the player sprite:
93 def add_enemy(self, delta_time: float):
94 """Adds a new enemy to the screen
95
96 Arguments:
97 delta_time {float} -- How much time has passed since the last call
98 """
99
100 # First, create the new enemy sprite
101 enemy = arcade.Sprite("images/missile.png", SCALING)
102
103 # Set its position to a random height and off screen right
104 enemy.left = random.randint(self.width, self.width + 80)
105 enemy.top = random.randint(10, self.height - 10)
.add_enemy() takes a single parameter, delta_time, which represents how much time has passed since the last time it was called. This is required by arcade.schedule(), and while you won’t use it here, it can be useful for applications that require advanced timing.
As with the player sprite, you first create a new arcade.Sprite with a picture and a scaling factor. You set the position using .left and .top to a random position somewhere off the screen to the right:
This allows the enemy to move onto the screen smoothly, rather than just appearing on the screen. Now, how do you make it move?
Moving Sprites
Moving a sprite requires you to change its position during the update phase of the game loop. While you can do this on your own, arcade has some built-in functionality to reduce your workload. Every arcade.Sprite not only has a set of position attributes, but it also has a set of motion attributes. Every time the sprite is updated, arcade will use the motion attributes to update the position, imparting relative motion to the sprite.
The Sprite.velocity attribute is a tuple consisting of the change in x and y positions. You can also access Sprite.change_x and Sprite.change_y directly. As mentioned above, every time the sprite is updated, its .position is changed based on the .velocity. All you need to do in .add_enemy() is set the velocity:
107 # Set its speed to a random speed heading left
108 enemy.velocity = (random.randint(-20, -5), 0)
109
110 # Add it to the enemies list
111 self.enemies_list.append(enemy)
112 self.all_sprites.append(enemy)
After you set the velocity to a random speed moving left on line 108, you add the new enemy to the appropriate lists. When you later call sprite.update(), arcade will handle the rest:
In your Python game design, enemies move in a straight line from right to left. Since your enemies are always moving left, once they’re off the screen, they’re not coming back. It would be good if you could get rid of an off-screen enemy sprite to free up memory and speed updates. Luckily, arcade has you covered.
Removing Sprites
Because your enemies are always moving left, their x positions are always getting smaller, and their y positions are always constant. Therefore, you can determine an enemy is off-screen when enemy.right is smaller than zero, which is the left edge of the window. Once you determine the enemy is off-screen, you call enemy.remove_from_sprite_lists() to remove it from all the lists to which it belongs and release that object from memory:
if enemy.right < 0:
enemy.remove_from_sprite_lists()
But when do you perform this check? Normally, this would happen right after the sprite moved. However, remember what was said earlier about the .all_enemies sprite list:
You use
.enemies_listto update the enemy positions and to check for collisions.
This means that in SpaceShooter.on_update(), you’ll call enemies_list.update() to handle the enemy movement automatically, which essentially does the following:
for enemy in enemies_list:
enemy.update()
It would be nice if you could add the off-screen check directly to the enemy.update() call, and you can! Remember, arcade is an object-oriented library. This means you can create your own classes based on arcade classes, and override the methods you want to modify. In this case, you create a new class based on arcade.Sprite and override .update() only:
17 class FlyingSprite(arcade.Sprite):
18 """Base class for all flying sprites
19 Flying sprites include enemies and clouds
20 """
21
22 def update(self):
23 """Update the position of the sprite
24 When it moves off screen to the left, remove it
25 """
26
27 # Move the sprite
28 super().update()
29
30 # Remove if off the screen
31 if self.right < 0:
32 self.remove_from_sprite_lists()
You define FlyingSprite as anything that will be flying in your game, like enemies and clouds. You then override .update(), first calling super().update() to process the motion properly. Then, you perform the off-screen check.
Since you have a new class, you’ll also need to make a small change to .add_enemy():
def add_enemy(self, delta_time: float):
"""Adds a new enemy to the screen
Arguments:
delta_time {float} -- How much time as passed since the last call
"""
# First, create the new enemy sprite
enemy = FlyingSprite("images/missile.png", SCALING)
Rather than creating a new Sprite, you create a new FlyingSprite to take advantage of the new .update().
Adding Clouds
To make your Python game more appealing visually, you can add clouds to the sky. Clouds fly through the sky, just like your enemies, so you can create and move them in a similar fashion.
.add_cloud() follows the same pattern as .add_enemy(), although the random speed is slower:
def add_cloud(self, delta_time: float):
"""Adds a new cloud to the screen
Arguments:
delta_time {float} -- How much time has passed since the last call
"""
# First, create the new cloud sprite
cloud = FlyingSprite("images/cloud.png", SCALING)
# Set its position to a random height and off screen right
cloud.left = random.randint(self.width, self.width + 80)
cloud.top = random.randint(10, self.height - 10)
# Set its speed to a random speed heading left
cloud.velocity = (random.randint(-5, -2), 0)
# Add it to the enemies list
self.clouds_list.append(cloud)
self.all_sprites.append(cloud)
Clouds move slower than enemies, so you calculate a lower random velocity on line 129.
Now your Python game looks a bit more complete:
Your enemies and clouds are created and move on their own now. Time to make the player move as well using the keyboard.
Keyboard Input
The arcade.Window class has two functions for processing keyboard input. Your Python game will call .on_key_press() whenever a key is pressed and .on_key_release() whenever a key is released. Both functions accept two integer parameters:
symbolrepresents the actual key that was pressed or released.modifiersdenotes which modifiers were down. These include the Shift, Ctrl, and Alt keys.
Luckily, you don’t need to know which integers represent which keys. The arcade.key module contains all of the keyboard constants you might want to use. Traditionally, moving a player with the keyboard uses one or more of three different sets of keys:
- The four arrow keys for Up, Down, Left, and Right
- The keys I, J, K, and L, which map to Up, Left, Down, and Right
- For left-hand control, the keys W, A, S, and D, which also map to Up, Left, Down, and Right
For this game, you’ll use the arrows and I/J/K/L. Whenever the user presses a movement key, the player sprite moves in that direction. When the user releases a movement key, the sprite stops moving in that direction. You also provide a way to quit the game using Q, and a way to pause the game using P. To accomplish this, you need to respond to keypresses and releases:
- When a key is pressed, call
.on_key_press(). In that method, you check which key was pressed:- If it’s Q, then you simply quit the game.
- If it’s P, then you set a flag to indicate the game is paused.
- If it’s a movement key, then you set the player’s
.change_xor.change_yaccordingly. - If it’s any other key, then you ignore it.
- When a key is released, call
.on_key_release(). Again, you check which key was released:- If it’s a movement key, then you set the player’s
.change_xor.change_yto 0 accordingly. - If it’s any other key, then you ignore it.
- If it’s a movement key, then you set the player’s
Here’s what the code looks like:
134 def on_key_press(self, symbol, modifiers):
135 """Handle user keyboard input
136 Q: Quit the game
137 P: Pause/Unpause the game
138 I/J/K/L: Move Up, Left, Down, Right
139 Arrows: Move Up, Left, Down, Right
140
141 Arguments:
142 symbol {int} -- Which key was pressed
143 modifiers {int} -- Which modifiers were pressed
144 """
145 if symbol == arcade.key.Q:
146 # Quit immediately
147 arcade.close_window()
148
149 if symbol == arcade.key.P:
150 self.paused = not self.paused
151
152 if symbol == arcade.key.I or symbol == arcade.key.UP:
153 self.player.change_y = 5
154
155 if symbol == arcade.key.K or symbol == arcade.key.DOWN:
156 self.player.change_y = -5
157
158 if symbol == arcade.key.J or symbol == arcade.key.LEFT:
159 self.player.change_x = -5
160
161 if symbol == arcade.key.L or symbol == arcade.key.RIGHT:
162 self.player.change_x = 5
163
164 def on_key_release(self, symbol: int, modifiers: int):
165 """Undo movement vectors when movement keys are released
166
167 Arguments:
168 symbol {int} -- Which key was pressed
169 modifiers {int} -- Which modifiers were pressed
170 """
171 if (
172 symbol == arcade.key.I
173 or symbol == arcade.key.K
174 or symbol == arcade.key.UP
175 or symbol == arcade.key.DOWN
176 ):
177 self.player.change_y = 0
178
179 if (
180 symbol == arcade.key.J
181 or symbol == arcade.key.L
182 or symbol == arcade.key.LEFT
183 or symbol == arcade.key.RIGHT
184 ):
185 self.player.change_x = 0
In .on_key_release(), you only check for keys that will impact your player sprite movement. There’s no need to check if the Pause or Quit keys were released.
Now you can move around the screen and quit the game immediately:
You may be wondering how the pause functionality works. To see that in action, you first need to learn to update all your Python game objects.
Updating the Game Objects
Just because you’ve set a velocity on all your sprites doesn’t mean they will move. To make them move, you have to update them over and over again in the game loop.
Since arcade controls the Python game loop, it also controls when updates are needed by calling .on_update(). You can override this method to provide the proper behavior for your game, including game movement and other behavior. For this game, you need to do a few things to update everything properly:
- You check if the game is paused. If so, then you can just exit, so no further updates happen.
- You update all your sprites to make them move.
- You check if the player sprite has moved off-screen. If so, then simply move them back on screen.
That’s it for now. Here’s what this code looks like:
189 def on_update(self, delta_time: float):
190 """Update the positions and statuses of all game objects
191 If paused, do nothing
192
193 Arguments:
194 delta_time {float} -- Time since the last update
195 """
196
197 # If paused, don't update anything
198 if self.paused:
199 return
200
201 # Update everything
202 self.all_sprites.update()
203
204 # Keep the player on screen
205 if self.player.top > self.height:
206 self.player.top = self.height
207 if self.player.right > self.width:
208 self.player.right = self.width
209 if self.player.bottom < 0:
210 self.player.bottom = 0
211 if self.player.left < 0:
212 self.player.left = 0
Line 198 is where you check if the game is paused, and simply return if so. That skips all the remaining code, so there will be no movement. All sprite movement is handled by line 202. This single line works for three reasons:
- Every sprite is a member of the
self.all_spriteslist. - The call to
self.all_sprites.update()results in calling.update()on every sprite in the list. - Every sprite in the list has
.velocity(consisting of the.change_xand.change_yattributes) and will process its own movement when its.update()is called.
Finally, you check if the player sprite is off-screen in lines 205 to 212 by comparing the edges of the sprites with the edges of the window. For example, on lines 205 and 206, if self.player.top is beyond the top of the screen, then you reset self.player.top to the top of the screen. Now that everything is updated, you can draw everything.
Drawing on the Window
Since updates to game objects happen in .on_update(), it seems appropriate that drawing the game objects would take place in a method called .on_draw(). Because you’ve organized everything into sprite lists, your code for this method is very short:
231 def on_draw(self):
232 """Draw all game objects
233 """
234 arcade.start_render()
235 self.all_sprites.draw()
All drawing starts with the call to arcade.start_render() on line 234. Just like updating, you can draw all your sprites at once by simply calling self.all_sprites.draw() on line 235. Now there’s just one final part of your Python game to work on, and it’s the very last part of the initial design:
When the player is hit by an obstacle, or the user closes the window, the game ends.
This is the actual game part! Right now, enemies will fly through your player sprite doing nothing. Let’s see how you can add this functionality.
Collision Detection
Games are all about collisions of one form or another, even in non-computer games. Without real or virtual collisions, there would be no slap-shot hockey goals, no double-sixes in backgammon, and no way in chess to capture your opponent’s queen on the end of a knight fork.
Collision detection in computer games requires the programmer to detect if two game objects are partially occupying the same space on the screen. You use collision detection to shoot enemies, limit player movement with walls and floors, and provide obstacles to avoid. Depending on the game objects involved and the desired behavior, collision detection logic can require potentially complicated math.
However, you don’t have to write your own collision detection code with arcade. You can use one of three different Sprite methods to detect collisions quickly:
Sprite.collides_with_point((x,y))returnsTrueif the given point(x,y)is within the boundary of the current sprite, andFalseotherwise.Sprite.collides_with_sprite(Sprite)returnsTrueif the given sprite overlaps with the current sprite, andFalseotherwise.Sprite.collides_with_list(SpriteList)returns a list containing all the sprites in theSpriteListthat overlap with the current sprite. If there are no overlapping sprites, then the list will be empty, meaning it will have a length of zero.
Since you’re interested in whether or not the single-player sprite has collided with any of the enemy sprites, the last method is exactly what you need. You call self.player.collides_with_list(self.enemies_list) and check if the list it returns contains any sprites. If so, then you end the game.
So, where do you make this call? The best place is in .on_update(), just before you update the positions of everything:
189 def on_update(self, delta_time: float):
190 """Update the positions and statuses of all game objects
191 If paused, do nothing
192
193 Arguments:
194 delta_time {float} -- Time since the last update
195 """
196
197 # If paused, don't update anything
198 if self.paused:
199 return
200
201 # Did you hit anything? If so, end the game
202 if self.player.collides_with_list(self.enemies_list):
203 arcade.close_window()
204
205 # Update everything
206 self.all_sprites.update()
Lines 202 and 203 check for a collision between the player and any sprite in .enemies_list. If the returned list contains any sprites, then that indicates a collision, and you can end the game. Now, why would you check before updating the positions of everything? Remember the sequence of action in the Python game loop:
- You update the states of the game objects. You do this in
.on_update(). - You draw all the game objects in their new positions. You do this in
.on_draw().
If you check for collisions after you update everything in .on_update(), then any new positions won’t be drawn if a collision is detected. You’re actually checking for a collision based on sprite positions that haven’t been shown to the user yet. It may appear to the player as though the game ended before there was an actual collision! When you check first, you ensure that what’s visible to the player is the same as the game state you’re checking.
Now you have a Python game that looks good and provides a challenge! Now you can add some extra features to help make your Python game stand out.
Extras
There are many more features you can add to your Python game to make it stand out. In addition to the features the game design called out that you didn’t implement, you may have others in mind as well. This section will cover two features that will give your Python game some added impact by adding sound effects and controlling the game speed.
Sound
Sound is an important part of any computer game. From explosions to enemy taunts to background music, your Python game is a little flat without sound. Out of the box, arcade provides support for WAV files. If the ffmpeg library is installed and available, then arcade also supports Ogg and MP3 format files. You’ll add three different sound effects and some background music:
- The first sound effect plays as the player moves up.
- The second sound effect plays when the player moves down.
- The third sound effect plays when there is a collision.
- The background music is the last thing you’ll add.
You’ll start with the sound effects.
Sound Effects
Before you can play any of these sounds, you have to load them. You do so in .setup():
66 # Spawn a new enemy every 0.25 seconds
67 arcade.schedule(self.add_enemy, 0.25)
68
69 # Spawn a new cloud every second
70 arcade.schedule(self.add_cloud, 1.0)
71
72 # Load your sounds
73 # Sound sources: Jon Fincher
74 self.collision_sound = arcade.load_sound("sounds/Collision.wav")
75 self.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav")
76 self.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")
Like your sprite images, it’s good practice to place all your sounds in a single sub-folder.
With the sounds loaded, you can play them at the appropriate time. For .move_up_sound and .move_down_sound, this happens during the .on_key_press() handler:
134 def on_key_press(self, symbol, modifiers):
135 """Handle user keyboard input
136 Q: Quit the game
137 P: Pause the game
138 I/J/K/L: Move Up, Left, Down, Right
139 Arrows: Move Up, Left, Down, Right
140
141 Arguments:
142 symbol {int} -- Which key was pressed
143 modifiers {int} -- Which modifiers were pressed
144 """
145 if symbol == arcade.key.Q:
146 # Quit immediately
147 arcade.close_window()
148
149 if symbol == arcade.key.P:
150 self.paused = not self.paused
151
152 if symbol == arcade.key.I or symbol == arcade.key.UP:
153 self.player.change_y = 5
154 arcade.play_sound(self.move_up_sound)
155
156 if symbol == arcade.key.K or symbol == arcade.key.DOWN:
157 self.player.change_y = -5
158 arcade.play_sound(self.move_down_sound)
Now, whenever the player moves up or down, your Python game will play a sound.
The collision sound will play whenever .on_update() detects a collision:
def on_update(self, delta_time: float):
"""Update the positions and statuses of all game objects
If paused, do nothing
Arguments:
delta_time {float} -- Time since the last update
"""
# If paused, don't update anything
if self.paused:
return
# Did you hit anything? If so, end the game
if len(self.player.collides_with_list(self.enemies_list)) > 0:
arcade.play_sound(self.collision_sound)
arcade.close_window()
# Update everything
self.all_sprites.update()
Just before the window closes, a collision sound will play.
Background Music
Adding background music follows the same pattern as adding sound effects. The only difference is when it starts to play. For background music, you normally start it when the level starts, so load and start the sound in .setup():
66 # Spawn a new enemy every 0.25 seconds
67 arcade.schedule(self.add_enemy, 0.25)
68
69 # Spawn a new cloud every second
70 arcade.schedule(self.add_cloud, 1.0)
71
72 # Load your background music
73 # Sound source: http://ccmixter.org/files/Apoxode/59262
74 # License: https://creativecommons.org/licenses/by/3.0/
75 self.background_music = arcade.load_sound(
76 "sounds/Apoxode_-_Electric_1.wav"
77 )
78
79 # Load your sounds
80 # Sound sources: Jon Fincher
81 self.collision_sound = arcade.load_sound("sounds/Collision.wav")
82 self.move_up_sound = arcade.load_sound("sounds/Rising_putter.wav")
83 self.move_down_sound = arcade.load_sound("sounds/Falling_putter.wav")
84
85 # Start the background music
86 arcade.play_sound(self.background_music)
Now, you not only have sound effects, but also some nifty background music as well!
Sound Limitations
There are some limitations on what arcade can currently do with sound:
- There is no volume control on any sounds.
- There is no way to repeat a sound, such as looping background music.
- There is no way to tell if a sound is currently playing before you try to stop it.
- Without
ffmpeg, you are limited to WAV sounds, which can be large.
Despite these limitations, it’s well worth the effort to add sound to your arcade Python game.
Python Game Speed
The speed of any game is dictated by its frame rate, which is the frequency at which the graphics on the screen are updated. Higher frame rates normally result in smoother gameplay, while lower frame rates give you more time to perform complex calculations.
The frame rate of an arcade Python game is managed by the game loop in arcade.run(). The Python game loop calls .on_update() and .on_draw() roughly 60 times per second. Therefore, the game has a frame rate of 60 frames per second or 60 FPS.
Notice the description above says that the frame rate is roughly 60 FPS. This frame rate is not guaranteed to be exact. It may fluctuate up or down based on many factors, such as load on the machine or longer-than-normal update times. As a Python game programmer, you want to ensure your Python game acts the same, whether it’s running at 60 FPS, 30 FPS, or any other rate. So how do you do this?
Time-Based Movement
Imagine an object moving in space at 60 kilometers per minute. You can calculate how far the object will travel in any length of time by multiplying that time by the object’s speed:
The object moves 120 kilometers in 2 minutes and 30 kilometers in half a minute.
You can use this same calculation to move your sprites at a constant speed no matter what the frame rate. If you specify the sprite’s speed in terms of pixels per second, then you can calculate how many pixels it moves every frame if you know how much time has passed since the last frame appeared. How do you know that?
Recall that .on_update() takes a single parameter, delta_time. This is the amount of time in seconds that have passed since the last time .on_update() was called. For a game running at 60 FPS, delta_time will be 1/60 of a second or roughly 0.0167 seconds. If you multiply the time that’s passed by the amount a sprite will move, then you’ll ensure sprite movement is based on the time elapsed and not the frame rate.
Updating Sprite Movement
There’s just one problem—neither Sprite.on_update() nor SpriteList.on_update() accept the delta_time parameter. This means there’s no way to pass this on to your sprites to handle automatically. Therefore, to implement this feature, you need to update your sprite positions manually. Replace the call to self.all_sprites.update() in .on_update() with the following code:
def on_update(self, delta_time: float):
"""Update the positions and statuses of all game objects
If paused, do nothing
Arguments:
delta_time {float} -- Time since the last update
"""
# If paused, don't update anything
if self.paused:
return
# Did you hit anything? If so, end the game
if len(self.player.collides_with_list(self.enemies_list)) > 0:
arcade.play_sound(self.collision_sound)
arcade.close_window()
# Update everything
for sprite in self.all_sprites:
sprite.center_x = int(
sprite.center_x + sprite.change_x * delta_time
)
sprite.center_y = int(
sprite.center_y + sprite.change_y * delta_time
)
In this new code, you modify the position of each sprite manually, multiplying .change_x and .change_y by delta_time. This ensures that the sprite moves a constant distance every second, rather than a constant distance every frame, which can smooth out gameplay.
Updating Sprite Parameters
Of course, this also means you should re-evaluate and adjust the initial position and speed of all your sprites as well. Recall the position and .velocity your enemy sprites are given when they’re created:
93 def add_enemy(self, delta_time: float):
94 """Adds a new enemy to the screen
95
96 Arguments:
97 delta_time {float} -- How much time as passed since the last call
98 """
99
100 # First, create the new enemy sprite
101 enemy = FlyingSprite("images/missile.png", SCALING)
102
103 # Set its position to a random height and off screen right
104 enemy.left = random.randint(self.width, self.width + 80)
105 enemy.top = random.randint(10, self.height - 10)
106
107 # Set its speed to a random speed heading left
108 enemy.velocity = (random.randint(-20, -5), 0)
With the new movement calculations based on time, your enemies will now move at a maximum speed of 20 pixels every second. This means that on an 800-pixel-wide window, the fastest enemy will take forty seconds to fly across the screen. Further, if the enemy starts eighty pixels to the right of the window, then the fastest will take four full seconds just to appear!
Adjusting the position and velocity is part of making your Python game interesting and playable. Start by adjusting each by a factor of ten, and readjust from there. The same reevaluation and adjustments should be done with the clouds, as well as the movement velocity of the player.
Tweaks and Enhancements
During your Python game design, there were several features that you didn’t add. To add to that list, here are some additional enhancements and tweaks that you may have noticed during Python gameplay and testing:
- When the game is paused, enemies and clouds are still generated by the scheduled functions. This means that, when the game is unpaused, a huge wave of them are waiting for you. How do you prevent that from happening?
- As mentioned above, due to some limitations of the
arcadesound engine, the background music does not repeat itself. How do you work around that issue? - When the player collides with an enemy, the game ends abruptly without playing the collision sound. How do you keep the game open for a second or two before it closes the window?
There may be other tweaks you could add. Try to implement some of them as an exercise, and share your results down in the comments!
A Note on Sources
You may have noticed a comment when the background music was loaded, listing the source of the music and a link to the Creative Commons license. This was done because the creator of that sound requires it. The license requirements state that, in order to use the sound, both proper attribution and a link to the license must be provided.
Here are some sources for music, sound, and art that you can search for useful content:
- OpenGameArt.org: sounds, sound effects, sprites, and other artwork
- Kenney.nl: sounds, sound effects, sprites, and other artwork
- Gamer Art 2D: sprites and other artwork
- CC Mixter: sounds and sound effects
- Freesound: sounds and sound effects
As you make your games and use downloaded content such as art, music, or code from other sources, please be sure that you’re complying with the licensing terms of those sources.
Conclusion
Computer games are a great introduction to coding, and the arcade library is a great first step. Designed as a modern Python framework for crafting games, you can create compelling Python game experiences with great graphics and sound.
Throughout this tutorial, you learned how to:
- Install the
arcadelibrary - Draw items on the screen
- Work with the
arcadePython game loop - Manage on-screen graphic elements
- Handle user input
- Play sound effects and music
- Describe how Python game programming in
arcadediffers frompygame
I hope you give arcade a try. If you do, then please leave a comment below, and Happy Pythoning! You can download all the materials used in this tutorial at the link below:
Download Assets: Click here to download the assets you'll use to make a game with arcade in this tutorial.
[ 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 ]
















