4.2 Variables and Data structures

Variables

Variables Are Labels - Variables are often described as boxes you can store values in - a variable references a certain value.

When declaring a variable we use the syntax: name = value

x = 1

We can also do multiple assignments

x, y, z = "A", "B", "C"

print(x, y, z)

Naming

When you are using variables in Python, you need to adhere to a few rules and guidelines. Breaking some of these rules will cause errors; other guidelines just help you write code that is easier to read and understand. Be sure to keep the following variable rules in mind:

  • Variable names can contain only letters, numbers, and underscores.

  • Variable names are case-sensitive (name, Name and NAME are three different variables)

  • They can start with a letter or an underscore, but not with a number. For instance, you can call a variable name_1 but not 1_name.

  • Spaces are not allowed in variable names, but underscores can be used to separate words in variable names. For example, first_name works, but first name will cause errors.

  • Avoid using Python keywords and function names as variable names; that is, do not use words that Python has reserved for a particular programmatic purpose, such as the word print. Read more: https://docs.python.org/3/reference/lexical_analysis.html#keywords

  • Variable names should be short but descriptive. For example, name is better than n, first_name is better than f_n, and name_length is better than length_of_persons_name.

  • Be careful when using the lowercase letter l and the uppercase letter O because they could be confused with the numbers 1 and 0.

Naming conventions

In Python naming conventions will not affect your code, but will affect your way for working in the team (in other languages naming convention matters)

We have 4 different ways of declaring:

  1. Snake case (ex: some_variable)

  2. Screaming Snake case (ex: SOME_VARIABLE)

  3. Pascal case (ex: SomeVariable)

  4. Camel case (ex: someVariable)

Read more about the Python conventions in https://peps.python.org/pep-0008/

I use them like this:

  • Snake case - for functions, methods, attributes, variables (this is the preferred way in Python)

Function names should be lowercase, with words separated by underscores as necessary to improve readability.

Is preferred def function_name(): not def functionName():.

first_name = input("Enter your name: ")
  • Screaming Snake case - Constants

PI = 3.14
  • Pascal case - defining class name.

class Student:
   def __init__(self, name, age):
      self.name = name
      self.age = age

   def displayInfo(self): # class method
      print('Student Name: ', self.name,', Age: ', self.age)
  • Camel case- to name some methods (ex: toJson, fromFile, updateFile) - because when you are using them they look like this Student.displayInfo

Data types

In Python3.6, PEP 498 introduces a new kind of string literals: f-strings, or formatted string literals.

Read more about f-strings https://docs.python.org/3/whatsnew/3.6.html#whatsnew36-pep498

1. Strings

In Python, strings are enclosed in either single or double quotation marks. Single quotes will not let variable pass, so be careful.

single_name = 'skillab'
doubleName = "skillab"

print("Course name is " + single_name)
# or
print(f"Course name is {single_name}")

String Manipulation

When we talk about string manipulation, we means either using the string methods or using the f-strings.

Keep in mind that some of the methods:
  • are case sensitive - title() and Title() are not the same

  • are irreversible - if you want to keep the original string you need to create a new variable - Example count() method

  • are not changing the original string - Example title() method

  • are badly documented - you need to read the documentation to understand what they are doing and how to use them properly. Example float’s method is_integer().

print("skillab".title())

name = "skillab"

print(name.title())
print(name.lower())
print(name.upper())
print(name.replace('a','A'))

# How to find out all the available alternatives
dir(name)

Methods available for strings:

'__add__',
'__class__',
'__contains__',
'__delattr__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getnewargs__',
'__getstate__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__mod__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__rmod__',
'__rmul__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'capitalize', # returns a copy of the string with its first character capitalized and the rest lowercased
'casefold', # returns a copy of the string with all its characters lowercased
'center', # returns a centered string of length width
'count', # returns the number of times a specified value occurs in a string
'encode', # returns an encoded version of the string
'endswith', # returns true if the string ends with the specified value
'expandtabs', # sets the tab size of the string
'find', # searches the string for a specified value and returns the position of where it was found
'format', # formats specified values in a string
'format_map', # formats specified values in a string
'index', # searches the string for a specified value and returns the position of where it was found
'isalnum', # returns True if all characters in the string are alphanumeric
'isalpha', # returns True if all characters in the string are in the alphabet
'isascii', # returns True if all characters in the string are ascii characters
'isdecimal', # returns True if all characters in the string are decimals
'isdigit', # returns True if all characters in the string are digits
'isidentifier', # returns True if the string is an identifier
'islower', # returns True if all characters in the string are lower case
'isnumeric', # returns True if all characters in the string are numeric
'isprintable', # returns True if all characters in the string are printable
'isspace', # returns True if all characters in the string are white spaces
'istitle', # returns True if the string follows the rules of a title
'isupper', # returns True if all characters in the string are upper case
'join', # joins the elements of an iterable to the end of the string
'ljust', # returns a left justified version of the string
'lower', # converts a string into lower case
'lstrip', # returns a left trim version of the string
'maketrans', # returns a translation table to be used in translations
'partition', # returns a tuple where the string is parted into three parts
'removeprefix', # removes a specified prefix from the string
'removesuffix', # removes a specified suffix from the string
'replace', # returns a string where a specified value is replaced with a specified value
'rfind', # searches the string for a specified value and returns the last position of where it was found
'rindex', # searches the string for a specified value and returns the last position of where it was found
'rjust', # returns a right justified version of the string
'rpartition', # returns a tuple where the string is parted into three parts
'rsplit', # splits the string at the specified separator, and returns a list
'rstrip', # returns a right trim version of the string
'split', # splits the string at the specified separator, and returns a list
'splitlines', # splits the string at line breaks and returns a list
'startswith', # returns true if the string starts with the specified value
'strip', # returns a trimmed version of the string
'swapcase', # swaps cases, lower case becomes upper case and vice versa
'title', # converts the first character of each word to upper case
'translate', # returns a translated string
'upper', # converts a string into upper case
'zfill' # fills the string with a specified number of 0 values at the beginning

Magic methods

..code-block:: python

__len__()

One of the many magic methods in the Python programming language, __len__ is primarily used to implement the len() function because every time the len() function is called, the magic method __len__ is also called internally.

After all is said and done, it returns an integer value larger than or equal to zero, which is the length of the object for which it was called.

print("skillab".__len__())
print(len("skillab"))

2. Numbers

Python supports 3 types of numbers:

  • integers(whole numbers)

  • floating point numbers(decimals).

  • complex numbers

# When declaring a variable we use the syntax
# name = value
x = 1
xint = int(x)
xfloat = float(x)
universe_age = 14_000_000_000

print(x)
print(xint)
print(xfloat)
print(universe_age)

print(type(x))
print(type(xint))
print(type(xfloat))

print(dir(x))
print(dir(xint))
print(dir(xfloat))

Methods available for numbers:

'__abs__',
'__add__',
'__bool__',
'__ceil__',
'__class__',
'__delattr__',
'__dir__',
'__divmod__',
'__doc__',
'__eq__',
'__float__',
'__floor__',
'__floordiv__',
'__format__',
'__ge__',
'__getattribute__',
'__getformat__',
'__getnewargs__',
'__getstate__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__int__',
'__le__',
'__lt__',
'__mod__',
'__mul__',
'__ne__',
'__neg__',
'__new__',
'__pos__',
'__pow__',
'__radd__',
'__rdivmod__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__rfloordiv__',
'__rmod__',
'__rmul__',
'__round__',
'__rpow__',
'__rsub__',
'__rtruediv__',
'__setattr__',
'__sizeof__',
'__str__',
'__sub__',
'__subclasshook__',
'__truediv__',
'__trunc__',
'as_integer_ratio', # returns a pair of integers whose ratio is exactly equal to the original float
'conjugate', # returns the complex conjugate of the float
'fromhex', # converts a hexadecimal string to a float
'hex', # converts a float to a hexadecimal string
'imag', # returns the imaginary part of a complex number
'is_integer', # returns True if the float is an integer
'real' # returns the real part of a complex number

Number operations

# add
print(1+2)
# subtract
print(1-2)
# multiply
print(1*2)
# divide
print(1/2)
# modulo
print(1%2)

3. Lists

A list is a collection of objects in a specific sequence. A list can contain anything you want, and there is no requirement that the items on it link to one another in any specific way. It’s a good idea to name your list in the plural, such as letters, numerals, or names, because lists typically contain many elements.

animals = ["cat", "dog", "python"]

print(animals)

# Lists are ordered collections, so you can access any element in a list by telling Python the position, or index, of the item desired. To access an element in a list, write the name of the list followed by the index of the item enclosed in square brackets.
print(animals[2])

# You can also format the result
print(animals[2].title())

Methods available for lists:

'__add__',
'__class__',
'__class_getitem__',
'__contains__',
'__delattr__',
'__delitem__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getstate__',
'__gt__',
'__hash__',
'__iadd__',
'__imul__',
'__init__',
'__init_subclass__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__mul__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__reversed__',
'__rmul__',
'__setattr__',
'__setitem__',
'__sizeof__',
'__str__',
'__subclasshook__',
'append', # adds an element at the end of the list
'clear', # removes all the elements from the list
'copy', # returns a copy of the list
'count', # returns the number of elements with the specified value
'extend', # add the elements of a list (or any iterable), to the end of the current list
'index', # returns the index of the first element with the specified value
'insert', # adds an element at the specified position
'pop', # removes the element at the specified position
'remove', # removes the first item with the specified value
'reverse', # reverses the order of the list
'sort' # sorts the list

List Manipulation

The majority of lists you create will be dynamic, meaning that when your program executes, you’ll add, update and remove items from the list you’ve created.

# Add elements
animals.append("monkey")
print(animals)

# Insert element at any position
animals.insert(0, "rabbit")
print(animals)

# Remove elements by position
del animals[3]
print(animals)

# Removing an Item Using the pop() Method
# Example want to remove a user from a list of active members and then add that user to a list of inactive members.
# pop() with no argument last element
# pop(1) 2nd element
popped_animal = animals.pop()

# Remove elements by value
animals.remove("python")

Warning

Only the first instance of the value you specify is removed by the remove() method. You’ll need to use a loop if there’s a chance that the value could appear more than once in the list to ensure that all instances are eliminated.

digits = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
min(digits)
max(digits)
sum(digits)

List Organization

Because you can’t always control the order in which your users submit their data, your lists will frequently be generated in an unpredictable order.

animals = ['rabbit', 'cat', 'dog', 'python', 'monkey']
# how long is the list
len(animals)

print(animals)

animals.sort()
animals.sort(reverse=True)

# sort() is a irreversible procedure
# if you need something temporary without affecting the original list use sorted(list_name)
print("Here is the original list:")
print(animals)

print("\nHere is the sorted list:")
print(sorted(animals))

print("\nHere is the original list again:")
print(animals)

Warning

When all the values are not lowercase, sorting a list alphabetically becomes a little more challenging. When determining a sort order, capital letters can be interpreted in a variety of ways, and specifying the precise order can be more difficult than we want to handle right now.

Avoiding Index Errors When Working with Lists

When you are working with lists for the first time, one type of error is frequent to see is Index error.

animals = ['rabbit', 'cat', 'dog', 'python', 'monkey']

print(animals[5])

# Instead of using last element by explicitly using position value we can use
print(animals[-1])

4. Dictionaries

In Python, a dictionary is a collection of key-value pairs. Each key has a value associated with it, and you may use a key to get that value. The value of a key could be an integer, string, list, dictionary, or even another dictionary. In actuality, you may use any Python-created object as a value in a dictionary.

What Are Dictionaries in Python? In Python, dictionaries are a built-in data type used to store collections of data. Unlike lists or tuples, dictionaries are unordered and store data in key-value pairs. Here’s a simple example:

When Are Dictionaries Used?

Dictionaries are highly flexible and useful in a variety of situations, such as:

  • Caching: To remember previously computed values.

  • Data grouping: To categorize and summarize data by specific keys.

  • Configuration: To hold settings and parameters.

  • JSON representation: Python dictionaries can easily be converted to JSON format, making them useful in web development.

  • Hash table implementations: To create sets, bags, or other abstract data types.

Best Practices

  • Key Uniqueness: Ensure each key in the dictionary is unique. Adding a duplicate key will overwrite the existing value for that key.

  • Immutable Keys: Use only immutable data types like strings, numbers, or tuples as keys.

  • Readability: Choose meaningful names for keys to make the code more understandable.

  • Exception Handling: Use methods like dict.get(key, default) or dict.setdefault(key, default) to avoid key errors.

  • Dictionary Comprehensions: Use dictionary comprehensions for concise and expressive creation of dictionaries.

  • Iteration: Use items(), keys(), and values() methods to iterate through dictionaries in a readable manner.

  • Updates: Use the update() method to merge two dictionaries, which is more efficient than looping through keys.

  • Deep Copy: Use copy.deepcopy() when you need to copy nested dictionaries.

Common Problems

  • KeyError: Trying to access a key that does not exist.

  • Memory Usage: Dictionaries can consume a lot of memory when they grow large.

  • Order Sensitivity: Prior to Python 3.7, dictionaries did not maintain order. Even now, relying on the insertion order could make your code less readable and harder to understand.

  • Type Errors: Using mutable or unhashable types as keys.

  • Concurrency: Dictionaries are not thread-safe, which can lead to unpredictable behavior in concurrent programs.

  • Inconsistency: Since dictionaries are mutable, changes in the dictionary can lead to bugs if not handled carefully, especially when the dictionary is being shared across functions or objects.

Methods available for dictionaries:

'__class__',
'__class_getitem__',
'__contains__',
'__delattr__',
'__delitem__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__getitem__',
'__getstate__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__ior__',
'__iter__',
'__le__',
'__len__',
'__lt__',
'__ne__',
'__new__',
'__or__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__reversed__',
'__ror__',
'__setattr__',
'__setitem__',
'__sizeof__',
'__str__',
'__subclasshook__',
'clear', # removes all the elements from the dictionary
'copy', # returns a copy of the dictionary
'fromkeys', # returns a dictionary with the specified keys and value
'get', # returns the value of the specified key
'items', # returns a list containing a tuple for each key value pair
'keys', # returns a list containing the dictionary's keys
'pop', # removes the element with the specified key
'popitem', # removes the last inserted key-value pair
'setdefault', # returns the value of the specified key. If the key does not exist: insert the key, with the specified value
'update', # updates the dictionary with the specified key-value pairs
'values' # returns a list of all the values in the dictionary
animals = { 'reptile': 'python', 'primates': 'monkey', 'mammal': 'dog'}

# Accessing elements using key
animals['primates']

# Accessing keys
animals.keys()

# Accessing values
animals.values()

# Loop through dictionary values
for animal in animals.values():
   print(animal.title())

muscle_cars = {
"brand": "Dodge",
"model": "Challenger",
"year": 1970
}

# Add new elements in dictionary
muscle_cars["color"] = "black"

# Update dictionary
muscle_cars.update({"color": "red"})

# Remove items
# `pop()` method removes the item with the specified key name
muscle_cars.pop("color")

# `popitem()` method removes the last inserted item
muscle_cars.pop("year")

# `del` keyword removes the item with the specified key name
del muscle_cars["model"]

# clear() method empties the dictionary
muscle_cars.clear()