Python match

Python match compares the value of an expression with patterns given in case blocks, when match exist value from that case block is returned, or command from that case block is executed. 

Example:

brand = input('Please enter car brand: ')

def discount_level(brand):

    match brand.upper():

        case 'FORD':

            return 0.1

        case 'RENAULT':

            return 0.05

        case 'BMW':

            return 0.08

        case _:

            return 0.03

print(f"Discount for {brand.upper()} is {discount_level(brand)} ")


#output sample

Please enter car brand: Renault

Discount for RENAULT is 0.05

Here we compare or match brand string (with capital, means brand.upper()  ) with patterns from case (FORD, RENAULT, BMW ). Anything else that is different than FORD, RENAULT, BMW will match the last case i.e. block "case _:". Character "_" it is like a wildcard.


In case block we can have one command or more commands, case block is "delimited" bassically according with indentation. Thus, in previous example, I modify a little bit and in case 'FORD' block I put at same indentation level two commands, for example now there is: 

brand = input('Please enter car brand: ')

def discount_level(brand):

    match brand.upper():

        case 'FORD':

            print('Based on the brand')

            return 0.1

        case 'RENAULT':

            return 0.05

        case 'BMW':

            return 0.08

        case _:

            return 0.03

print(f"Discount for {brand.upper()} is {discount_level(brand)} ")


#sample output for above:

Please enter car brand: Ford

Based on the brand

Discount for FORD is 0.1 


In the previous sample it matches brand with one of value from case statement. Nevertheless, in case statement we can have multiple values. For example, if we want to give a discount of 10% (i.e. 0.1) for brands: Ford, Skoda, Opel we can use "|" operator and example become: 

brand = input('Please enter car brand: ')

def discount_level(brand):

    match brand.upper():

        case 'FORD'|'SKODA'|'OPEL':

            return 0.1

        case 'RENAULT':

            return 0.05

        case 'BMW':

            return 0.08

        case _:

            return 0.03

print(f"Discount for {brand.upper()} is {discount_level(brand)} ")


#sample output for above:

Please enter car brand: Opel

Discount for OPEL is 0.1 


In examples till now we compared brand, i.e. a string, similar is working if we compare numbers, or even bool, but match statement can compare more complex structures. For classes or objects an example is: 

class CarFeature:

    numSpeed: int

    maxSpeed: int

    acc100: float

    def __init__(self, numSpeed, maxSpeed, acc100):

        self.numSpeed = numSpeed

        self.maxSpeed = maxSpeed

        self.acc100 = acc100


def car_q(carfeature):

    match carfeature:

        case CarFeature(numSpeed=6, maxSpeed = 250, acc100 = 9.9):

            print("High class car")

        case CarFeature(numSpeed=5, maxSpeed = 175, acc100 = 11.5):

            print("Entry level class car")

        case _:

            print("Middle level class car")


carfeature1 = CarFeature(6, 260, 9.8)

print(f"car 1 with numSpeed: {carfeature1.numSpeed}, maxSpeed: {carfeature1.maxSpeed}, acc100: {carfeature1.acc100} is")

car_q(carfeature1)


#Output of code: 

car 1 with numSpeed: 6, maxSpeed: 260, acc100: 9.8 is

Middle level class car

In this example we defined a class named CarFeature. Then we defined a function car_q that uses match statement which in this case compares or has in case "instances" of the CarFeature class. 

Then we created a carfeature1 instance of class CarFeature, and used carfeature1 to test car_q function.

This match example compares instances of classes or objects. Basically, match can compare any pattern that is according with pattern matching PEP 636 specifications.

Accessing API in Python

There are many internets API which offer data in json format. Such data can be easily used in python using python requests module. In the current post we will use requests python module toghether with Json format to find prognosed maximum temperature per day, for next 8 days based on openweathermap API. Documentation for requests module can be consulted in many links on internet, 

for example, here or in this site.

At the date of writing this short intro in accessing API and using output in JSON format, latest requests release can be found for example on community update link.

If not yet installed, python requests modul as usual can be installed via pip: 

python -m pip install requests

We will exemplify how to use requests and get JSON data for openweathermap.org API. 

openweathermap have many API, just checking on openweathermap website we will exemplify for "Current Weather Data" which have documentation at API doc 

Before doing something in Python for many of the presented endpoints we will need to get an API key, and understand API call. For API key, we need to create an account or profile on openweathermap.org site, after that in the profile it is a link where we can obtain an API key. This is easy, and let's presume we get it and now we have an API key.

For call current weather data API, based on documentation API call is 

https://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={API key}

In this URL:

  • endpoint is https://api.openweathermap.org/data/2.5/weather
  • parameters are:
  • lat for latitude
  • lon for longitude
  • appid is the API key discussed before
  • in  documentation exist other parameters like mode, units, lang

Parameters lat and lon can be obtained from internat, example from this site: https://www.latlong.net/, or from here https://www.gps-coordinates.net/, there are also some others. 

For lat 44.451209, lon 26.13391 and appid 70g04e4613056b159c2761a9d9e664d2 url for call is

https://api.openweathermap.org/data/2.5/onecall?lat=44.451209&lon=26.13391&appid=70g04e4613056b159c2761a9d9e664d2

Observation: appid is a fictive one here, you will need to obtain one from openweathermap site as mentioned prior. 

Loading it in browser we obtain:

Weather API In Browser

Figure 1: Weather API loaded In Browser

In this image it is a bunch of data, it looks difficult to use it effectively. Using python, we overcome this and we can extract or use data that we need. Simple call to this API using Python is in below code:

import requests

Weather_url = "https://api.openweathermap.org/data/2.5/onecall"

api_key = "70g04e4613056b159c2761a9d9e664d2"

weather_params = {

    "lat": 44.451209,

    "lon": 26.13391,

    "appid": api_key,

}

response = requests.get(Weather_url, params=weather_params)

We used get function from requests module, there is requests.get(.....)

Like parameters for get I used:

  • string Weather_url which is string with endpoint as mentioned in API doc
  • params which contain API parameters according with documentation. This "params" is a dictionary that contain parameters (lat, lon and appid ) means requests.get accept params like a dictionary with pairs key:value.

The output of the code is just "Process finished with exit code 0" and for the begining we can think it is not too useful. We can use supplimentary response.status_code it will give: 

print(response.status_code)

#output 

200

Here status_code  give well know response status code universal for any http request (example of other status code: 201, 401, 404 etc). For a complete list see: "List of HTTP status codes

In instruction: response = requests.get(Weather_url, params=weather_params)

response is an object, a class. If in GUI environment we just type response, intelisense will show a bunch of methods and variable for this class like below:  

Some methods from response module

Figure 2: Some methods from response module

Using response.json() will give in python output exact json data that we see in Figure 1, but this is displayed in a single line like:

{'lat': 44.4512, 'lon': 26.1339, 'timezone': 'Europe/Bucharest', 'timezone_offset': 10800.........text omitted ............ }

means:

Json output

Figure 3: Json output

This data still seems hard to analyze, however as we see there it seems similar with a dictionary, we can see structure of this in a Json viewer. 

There are many online Json viewers, I selected first Json data from Figure 1 using CTRL+A, press CTRL+C and then go to Json online https://jsonformatter.org/json-viewer viewer and press CTRL+V and there is:

Data In Json Viewer

Figure 4: Data in Json Viewer

We understand from here easy json structure where we see {} there is about dictionary, where is [] there is about list. 

Looking to Figure 4 if we need to access daily[0] list we will write

weather_1=response.json() # this is entire json data output

print(weather_1["daily"][0])

Expanding further there is: 

Maximum temperature for a day in Json output

Figure 5: Maximum temperature for a day in Json output

We see struture, hence for accessing daily max temperature for day 0 we will use 

weather_1=response.json() # this is entire json data output

print(weather_1["daily"][0]["temp"]["max"])

it will display 283.93 max temperature for daily[0]. 

Observation: above temperature is in Kelvin scale (to convert to celsius degtree we need to use "Kelvin value" - 273.15)


The code for find maximum temperature in next 8 days is:

import requests

Weather_Endpoint = "https://api.openweathermap.org/data/2.5/onecall"

api_key = "70g04e4613056b159c2761a9d9e664d2"

weather_params = {

    "lat": 44.451209,

    "lon": 26.13391,

    "appid": api_key,

}

response = requests.get(Weather_Endpoint, params=weather_params)

response.raise_for_status()

weather_1=response.json()

weather_2_daily = weather_1["daily"]

daily_max_temp=[]

for i in range(8):

    daily_max_temp.append(float(weather_2_daily[i]["temp"]["max"]))

    print(f"Maximum temperature in day {i} is {round(daily_max_temp[i]-273.15, 2)} Celsius degree")

print(f"Maximum daily temperature in next 8 days is {round(max(daily_max_temp)-273.15,2)} Celsius degree")


#output is: 

Maximum temperature in day 0 is 11.09 Celsius degree

Maximum temperature in day 1 is 14.41 Celsius degree

Maximum temperature in day 2 is 16.2 Celsius degree

Maximum temperature in day 3 is 17.99 Celsius degree

Maximum temperature in day 4 is 13.5 Celsius degree

Maximum temperature in day 5 is 13.01 Celsius degree

Maximum temperature in day 6 is 16.81 Celsius degree

Maximum temperature in day 7 is 16.82 Celsius degree

Maximum daily temperature in next 8 days is 17.99 Celsius degree

About the code: 

- weather_1 represent entire json output data

- weather_2_daily=weather_1["daily"] is a list based on Figure 4

- we defined an empty list with daily_max_temp=[]

- weather_2_daily[i]["temp"]["max"] is basicaly maximum daily temperature in day "i" in Kelvin degree.

Built in functions: next, bool

next(iterator)

Python built-in function next is used to retrieve next item from an iterator, if it reaches iterator end built-in exception StopIteration is raised. 

Example:

list1 = [23, True, 'yes']

myIterator = iter(list1)

#

item = next(myIterator)

print(f"First item is: {item}")

#

item = next(myIterator)

print(f"Item is: {item}")

#

item = next(myIterator)

print(f"Last item is: {item}")

item = next(myIterator)


#Output

First item is: 23

Item is: True

Last item is: yes

Traceback (most recent call last):

  File "test.py", line 9, in <module>

    item = next(myIterator)

StopIteration

In this example we create iterator myIterator from list1. Then we run next funtion trice using item = next(myIterator) and print item with a f string. myIterator have 3 items, after running trice next function we are at the end of it, once try to run the fourth time next function, StopIteration built-in exception is raised. 


class bool(x=False)

The method return a boolean value based on standard "truth testing procedure". Basically this procedure will return True if argument is a number (excepting 0), a string or True itself. It will return False if argument is False, 0 or None, empty. "Truth testing procedure" in python area is comprehensive described here


Examples:

myarg1=12

print(bool(myarg1))

# output: True


myarg2='string123'

print(bool(myarg2))

# output: True


myarg3=True

print(bool(myarg3))

# output: True


myarg4=False

print(bool(myarg4))

# output: False


myarg5=0

print(bool(myarg5))

# output: False


myarg6=None

print(bool(myarg6))

# output: False


print(bool())

# output: False

Built in functions: all, filter and slice

all(iterable)

Function accepts like argument an iterable structure (like list, tuple, dictionary, etc), function returns True if all elements of the iterable structure are true.

Example:

mylist1 = [1, 'two', 'xyz', 8.35, True]

mylist2 = [1, 'two', 'xyz', 8.35, 'yhg']

mylist3 = [1, 0, 'xyz', 8.35, 'test']

mylist4 = [1, 'tt', False, 8.35, 'test']


print(f'all(mylist1) give: {all(mylist1)}')

print(f'all(mylist2) give: {all(mylist2)}')

print(f'all(mylist3) give: {all(mylist3)}')

print(f'all(mylist4) give: {all(mylist4)}')


output: 

all(mylist1) give: True

all(mylist2) give: True

all(mylist3) give: False

all(mylist4) give: False

Here all(mylist1) and all(mylist2) return True because all elements of mylist1 and mylist2 are True (anything that is not False or 0 is considered true).

all(mylist3) return False because mylist3 contain 0 element. all(mylist4) return False because mylist4 contain False.


filter(function, iterable)

Python built-in filter function construct an itertor based on an iterable parameter like list, tuple, etc, taking from it only elements that fulfil function parameter condition. Description sounds a little bit complicated, but some examples show that filter function is simple. 

Let's presume we have list1 = [45, 12, 5, 7, 32, 20, 2, 18, 21, 9, 16]

We need to obtain from list1 a new list that should contain only numbers greater than 15. Code for this is:

list1 = [45, 12, 5, 7, 32, 20, 2, 18, 21, 9, 16]

# filter function for number greater than 15

def fg15(x):

    if x>15:

        return x

# apply filter function

result=filter(fg15, list1)

list2=list(result)

print(list2)

 

output:

[45, 32, 20, 18, 21, 16]

We observe here that list2 contains elements from list1 that are greater than 15 (which fulfil filter restriction).


class slice ([start], stop[, step]) 

Explanation of slice according with python documentation https://docs.python.org/3/library/functions.html#slice is a little bit complicated. 

Slice function returns a "slice object" representing a set of indices similar to range(start, stop, step). Step parameter is optional, if not specified it is 1. Start is optional, if it is not specified start is first element. It is easy to understand slice by examples:

list1 = (23, True, 5, 12, 'yes', 'cat', 'dog', 34)

slc1=slice(1,6,2)


print(slc1) # here slc1 is the slice objet

print(list1[slc1])

print(list1[1:6:2]) 


output: 

slice(1, 6, 2)

(True, 12, 'cat')

(True, 12, 'cat')