100 Helpful Python Tips You Can Learn Before Finishing Your Morning Coffee | by Fatos Morina | Towards Data Science

Python is quite popular nowadays, mainly due to its simplicity, and easiness to learn.
You can use it for a wide range of tasks like data science and machine learning, web development, scripting, automation, etc.
Since this is a pretty long article and you want to reach its end before you finish your coffee, why don’t we just start?
Okay, here we go...
Despite all the Python code that you have seen so far, chances are that you may have missed the following “for-else” which I also got to see for the first time a couple of weeks ago.
This is a “for-else” method of looping through a list, where despite having an iteration through a list, you also have an “else” condition, which is quite unusual.
This is not something that I have seen in other programming languages like Java, Ruby, or JavaScript.
Let’s see an example of how it looks in practice.
Let’s say that we are trying to check whether there are no odd numbers in a list.
Let’s iterate through it:
numbers = [2, 4, 6, 8, 1]for number in numbers:    if number % 2 == 1:        print(number)        breakelse:    print("No odd numbers")
In case we find an odd number, then that number will be printed since break will be executed and the else branch will be skipped.
Otherwise, if break is never executed, the execution flow will continue with the else branch.
In this example, we are going to print 1.
my_list = [1, 2, 3, 4, 5]one, two, three, four, five = my_list
import heapqscores = [51, 33, 64, 87, 91, 75, 15, 49, 33, 82]print(heapq.nlargest(3, scores))  # [91, 87, 82]print(heapq.nsmallest(5, scores))  # [15, 33, 33, 49, 51]
We can extract all elements of a list using “*”:
my_list = [1, 2, 3, 4]print(my_list)  # [1, 2, 3, 4]print(*my_list)  # 1 2 3 4
This can be helpful in situations where we want to pass all elements of a list as method arguments:
def sum_of_elements(*arg):    total = 0    for i in arg:        total += i    return totalresult = sum_of_elements(*[1, 2, 3, 4])print(result)  # 10
_, *elements_in_the_middle, _ = [1, 2, 3, 4, 5, 6, 7, 8]print(elements_in_the_middle)  # [2, 3, 4, 5, 6, 7]
one, two, three, four = 1, 2, 3, 4
You can loop through the elements in a list in a single line in a very comprehensive way.
Let’s see that in action in the following example:
numbers = [1, 2, 3, 4, 5, 6, 7, 8]even_numbers = [number for number in numbers if number % 2 == 0]print(even_numbers)  # [2, 4, 6, 8]
We can do the same with dictionaries, sets, and generators.
Let’s see another example, but now for dictionaries.
dictionary = {'first_element': 1, 'second_element': 2,              'third_element': 3, 'fourth_element': 4}odd_value_elements = {key: num for (key, num) in                      dictionary.items() if num % 2 == 1}print(odd_value_elements)  # {'first_element': 1, 'third_element': 3}
This example was inspired by this article.
From the docs:
An Enum is a set of symbolic names bound to unique values. They are similar to global variables, but they offer a more useful repr(), grouping, type-safety, and a few other features.
Here is an example:
from enum import Enumclass Status(Enum):    NO_STATUS = -1    NOT_STARTED = 0    IN_PROGRESS = 1    COMPLETED = 2print(Status.IN_PROGRESS.name)  # IN_PROGRESSprint(Status.COMPLETED.value)  # 2
string = "Abc"print(string * 5)  # AbcAbcAbcAbcAbc
If you have a value and you want to compare it whether it is between two other values, there is a simple expression that you use in Math:
1 < x < 10
That is the algebraic expression that we learn in elementary school. However, you can also use that same expression in Python as well.
Yes, you heard that right. You have probably done comparisons of such form up until now:
1 < x and x < 10
For that, you simply need to use the following in Python:
1 < x < 10
This doesn’t work in Ruby, the programming language that was developed with the intention of making programmers happy. This turns out to be working in JavaScript as well.
I was really impressed seeing such a simple expression not being talked about more widely. At least, I haven’t seen it being mentioned that much.
This is available as of Python 3.9:
first_dictionary = {'name': 'Fatos', 'location': 'Munich'}second_dictionary = {'name': 'Fatos', 'surname': 'Morina',                     'location': 'Bavaria, Munich'}result = first_dictionary | second_dictionaryprint(result)  # {'name': 'Fatos', 'location': 'Bavaria, Munich', 'surname': 'Morina'}
books = ('Atomic habits', 'Ego is the enemy', 'Outliers', 'Mastery')print(books.index('Mastery'))   # 3
Let’s say that you get the input in a function that is a string, but it is supposed to be a list:
input = "[1,2,3]"
You don’t need it in that format. You need it to be a list:
input = [1,2,3]
Or maybe you the following response from an API call:
input = [[1, 2, 3], [4, 5, 6]]
Rather than bothering with complicated regular expressions, all you have to do is import the module ast and then call its method literal_eval:
import astdef string_to_list(string):    return ast.literal_eval(string)
That’s all you need to do.
Now you will get the result as a list, or list of lists, namely like the following:
import astdef string_to_list(string):    return ast.literal_eval(string)string = "[[1, 2, 3],[4, 5, 6]]"my_list = string_to_list(string)print(my_list)  # [[1, 2, 3], [4, 5, 6]]
Let’s assume that you want to find the difference between 2 numbers. The difference is not commutative:
a - b != b -a
However, we may forget the ordering of the parameters which can cause “trivial” mistakes:
def subtract(a, b):    return a - bprint((subtract(1, 3)))  # -2print((subtract(3, 1)))  # 2
To avoid such potential mistakes, we can simply use named parameters and the ordering of parameters doesn’t matter anymore:
def subtract(a, b):    return a - bprint((subtract(a=1, b=3)))  # -2print((subtract(b=3, a=1)))  # -2
print(1, 2, 3, "a", "z", "this is here", "here is something else")
print("Hello", end="")print("World")  # HelloWorldprint("Hello", end=" ")print("World")  # Hello Worldprint('words',   'with', 'commas', 'in', 'between', sep=', ')# words, with, commas, in, between
You can do advanced printing quite easily:
print("29", "01", "2022", sep="/")  # 29/01/2022print("name", "domain.com", sep="@")  # name@domain.com
four_letters = “abcd” # this works4_letters = “abcd” # this doesn’t work
+variable = “abcd”  # this doesn’t work
number = 0110 # this doesn't work
This means, anywhere you want, as many times as you want in the name of a variable:
a______b = "abcd"  # this works_a_b_c_d = "abcd"  # this also works
I am not encouraging you to use it, but in case you see a weird naming of a variable like that, know that it is actually a valid name of a variable.
This way it can be easier to read them.
print(1_000_000_000)  # 1000000000print(1_234_567)  # 1234567
my_list = ['a', 'b', 'c', 'd']my_list.reverse()print(my_list)  # ['d', 'c', 'b', 'a']
my_string = "This is just a sentence"print(my_string[0:5])  # This# Take three steps forwardprint(my_string[0:10:3])  # Tsse
my_string = "This is just a sentence"print(my_string[10:0:-1])  # suj si sih# Take two steps forwardprint(my_string[10:0:-2])  # sjs i
Indices indicating the start and end of slicing can be optional.
my_string = "This is just a sentence"print(my_string[4:])  # is just a sentenceprint(my_string[:3])  # Thi
print(3/2)  # 1.5print(3//2)  # 1
“is” checks whether 2 variables are pointing to the same object in memory.
“==” compares the equality of values that these 2 objects hold.
first_list = [1, 2, 3]second_list = [1, 2, 3]# Is their actual value the same?print(first_list == second_list)  # True# Are they pointing to the same object in memoryprint(first_list is second_list)  # False, since they have same values, but in different objects in memorythird_list = first_listprint(third_list is first_list)  # True, since both point to the same object in memory
dictionary_one = {"a": 1, "b": 2}dictionary_two = {"c": 3, "d": 4}merged = {**dictionary_one, **dictionary_two}print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
first = "abc"second = "def"print(first < second)  # Truesecond = "ab"print(first < second)  # False
my_string = "abcdef"print(my_string.startswith("b"))  # False
print(id(1))  # 4325776624print(id(2))  # 4325776656print(id("string"))  # 4327978288
When we assign a variable to an immutable type such as integers, floats, strings, booleans, and tuples, then this variable points to an object in memory.
In case we assign to that variable another value, the original object is still in memory, but the variable pointing to it is lost:
number = 1print(id(number))  # 4325215472print(id(1))  # 4325215472number = 3print(id(number))  # 4325215536print(id(1))  # 4325215472
This was already mentioned in the previous point but wanted to emphasize it since this is quite important.
name = "Fatos"print(id(name))  # 4422282544name = "fatos"print(id(name))  # 4422346608
my_tuple = (1, 2, 3, 4)print(id(my_tuple))  # 4499290128my_tuple = ('a', 'b')print(id(my_tuple))  # 4498867584
This means that we can change the object without losing binding to it:
cities = ["Munich", "Zurich", "London"]print(id(cities))  # 4482699712cities.append("Berlin")print(id(cities))  # 4482699712
Here is another example with sets:
my_set = {1, 2, 3, 4}print(id(my_set))  # 4352726176my_set.add(5)print(id(my_set))  # 4352726176
This way, you can no longer modify it:
my_set = frozenset(['a', 'b', 'c', 'd'])my_set.add("a")
If you do that, an error will be thrown:
AttributeError: 'frozenset' object has no attribute 'add'
However, “elif” cannot stand on its own without an “if” step before it:
def check_number(number):    if number > 0:        return "Positive"    elif number == 0:        return "Zero"    return "Negative"print(check_number(1))  # Positive
def check_if_anagram(first_word, second_word):    first_word = first_word.lower()    second_word = second_word.lower()    return sorted(first_word) == sorted(second_word)print(check_if_anagram("testinG", "Testing"))  # Trueprint(check_if_anagram("Here", "Rehe"))  # Trueprint(check_if_anagram("Know", "Now"))  # False
print(ord("A"))  # 65print(ord("B"))  # 66print(ord("C"))  # 66print(ord("a"))  # 97
dictionary = {"a": 1, "b": 2, "c": 3}keys = dictionary.keys()print(list(keys))  # ['a', 'b', 'c']
dictionary = {"a": 1, "b": 2, "c": 3}values = dictionary.values()print(list(values))  # [1, 2, 3]
dictionary = {"a": 1, "b": 2, "c": 3}reversed_dictionary = {j: i for i, j in dictionary.items()}print(reversed)  # {1: 'a', 2: 'b', 3: 'c'}
print(int(False))  # 0print(float(True))  # 1.0
“False” is 0, whereas “True” is 1.
x = 10y = 12result = (x - False)/(y * True)print(result)  # 0.8333333333333334
print(bool(.0))  # Falseprint(bool(3))  # Trueprint(bool("-"))  # Trueprint(bool("string"))  # Trueprint(bool(" "))  # True
print(complex(10, 2))  # (10+2j)
You can also convert a number into a hexadecimal number:
print(hex(11))  # 0xb
If you use append(), you are going to insert new values from the right.
We can also use insert() to specify the index and the element where we want to insert this new element. In our case, we want to insert it in the first position, so we use 0 as the index:
my_list = [3, 4, 5]my_list.append(6)my_list.insert(0, 2)print(my_list)  # [2, 3, 4, 5, 6]
You cannot have lambdas in more than one line.
Let’s try the following:
comparison = lambda x: if x > 3:                    print("x > 3")                else:                    print("x is not greater than 3")
The following error is going to be thrown:
result = lambda x: if x > 3: ^SyntaxError: invalid syntax
Let’s try the following:
comparison = lambda x: "x > 3" if x > 3
We are going to get the following error:
comparison = lambda x: "x > 3" if x > 3                                          ^SyntaxError: invalid syntax
Noe that this is a feature of the conditional expression and not of the lambda itself.
my_list = [1, 2, 3, 4]odd = filter(lambda x: x % 2 == 1, my_list)print(list(odd))   # [1, 3]print(my_list)  # [1, 2, 3, 4]
my_list = [1, 2, 3, 4]squared = map(lambda x: x ** 2, my_list)print(list(squared))   # [1, 4, 9, 16]print(my_list)  # [1, 2, 3, 4]
for number in range(1, 10, 3):    print(number, end=" ")# 1 4 7
So you don’t need to include it at all.
def range_with_zero(number):    for i in range(0, number):        print(i, end=' ')def range_with_no_zero(number):    for i in range(number):        print(i, end=' ')range_with_zero(3)  # 0 1 2range_with_no_zero(3)  # 0 1 2
If the length is greater than 0, then it is by default True, so you don’t really need to compare it with 0:
def get_element_with_comparison(my_list):    if len(my_list) > 0:        return my_list[0]def get_first_element(my_list):    if len(my_list):        return my_list[0]elements = [1, 2, 3, 4]first_result = get_element_with_comparison(elements)second_result = get_element_with_comparison(elements)print(first_result == second_result)  # True
However, only the last one is called, since it overrides previous ones.
def get_address():    return "First address"def get_address():    return "Second address"def get_address():    return "Third address"print(get_address())  # Third address
class Engineer:    def __init__(self, name):        self.name = name        self.__starting_salary = 62000dain = Engineer('Dain')print(dain._Engineer__starting_salary)  # 62000
import sysprint(sys.getsizeof("bitcoin"))  # 56
def get_sum(*arguments):    result = 0    for i in arguments:        result += i    return resultprint(get_sum(1, 2, 3))  # 6print(get_sum(1, 2, 3, 4, 5))  # 15print(get_sum(1, 2, 3, 4, 5, 6, 7))  # 28
Calling the parent’s class initializer using super():
class Parent:    def __init__(self, city, address):        self.city = city        self.address = addressclass Child(Parent):    def __init__(self, city, address, university):        super().__init__(city, address)        self.university = universitychild = Child('Zürich', 'Rämistrasse 101', 'ETH Zürich')print(child.university)  # ETH Zürich
Calling the parent’s class using the parent class’s name:
class Parent:    def __init__(self, city, address):        self.city = city        self.address = addressclass Child(Parent):    def __init__(self, city, address, university):        Parent.__init__(self, city, address)        self.university = universitychild = Child('Zürich', 'Rämistrasse 101', 'ETH Zürich')print(child.university)  # ETH Zürich
Note that calls to parent initializers using __init__() and super() can only be used inside the child class’s initializer.
Whenever you use the + operator between two int data types, then you are going to find their sum.
However, when you use it between two string data types, you are going to merge them:
print(10 + 1)  # Adding two integers using '+'print('first' + 'second')  # Merging two strings '+'
This represents the operator overloading.
You can also use it with your own classes as well:
class Expenses:    def __init__(self, rent, groceries):        self.rent = rent        self.groceries = groceries    def __add__(self, other):        return Expenses(self.rent + other.rent,                        self.groceries + other.groceries)april_expenses = Expenses(1000, 200)may_expenses = Expenses(1000, 300)total_expenses = april_expenses + may_expensesprint(total_expenses.rent)  # 2000print(total_expenses.groceries)  # 500
Here is another example of an operation overloadding that you can define yourself:
class Game:    def __init__(self, score):        self.score = score    def __lt__(self, other):        return self.score < other.scorefirst = Game(1)second = Game(2)print(first < second)  # True
Similarly, like the two previous cases, we can override the __eq__() function based on our own needs:
class Journey:    def __init__(self, location, destination, duration):        self.location = location        self.destination = destination        self.duration = duration    def __eq__(self, other):        return ((self.location == other.location) and                (self.destination == other.destination) and                (self.duration == other.duration))first = Journey('Location A', 'Destination A', '30min')second = Journey('Location B', 'Destination B', '30min')print(first == second)
You can also analogously define:
__sub__() for -
__mul__() for *
__truediv__() for /
__ne__() for !=
__ge__() for >=
__gt__() for >
class Rectangle:    def __init__(self, a, b):        self.a = a        self.b = b    def __repr__(self):        return repr('Rectangle with area=' + str(self.a * self.b))print(Rectangle(3, 4))  # 'Rectangle with area=12'
string = "This is just a sentence."result = string.swapcase()print(result)  # tHIS IS JUST A SENTENCE.
string = "  "result = string.isspace()print(result)  # True
name = "Password"print(name.isalnum())  # True, because all characters are alphabetsname = "Secure Password "print(name.isalnum())  # False, because it contains whitespacesname = "S3cur3P4ssw0rd"print(name.isalnum())  # Truename = "133"print(name.isalnum())  # True, because all characters are numbers
string = "Name"print(string.isalpha())  # Truestring = "Firstname Lastname"print(string.isalpha())  # False, because it contains whitespacestring = “P4ssw0rd”print(string.isalpha())  # False, because it contains numbers
string = "This is a sentence with       "# Remove trailing spaces from the rightprint(string.rstrip())  # "This is a sentence with"string = "this here is a sentence.....,,,,aaaaasd"print(string.rstrip(“.,dsa”))  # "this here is a sentence"
You can similarly remove characters from the left based on the argument:
string = "ffffffffFirst"print(string.lstrip(“f”))  # First
string = "seven"print(string.isdigit())  # Falsestring = "1337"print(string.isdigit())  # Truestring = "5a"print(string.isdigit())  # False, because it contains the character 'a'string = "2**5"print(string.isdigit())  # False
# 42673 in Arabic numeralsstring = "四二六七三"print(string.isdigit())  # Falseprint(string.isnumeric())  # True
string = "This is a sentence"print(string.istitle())  # Falsestring = "10 Python Tips"print(string.istitle())  # Truestring = "How to Print A String in Python"# False, because of the first characters being lowercase in "to" and "in"print(string.istitle())string = "PYTHON"print(string.istitle())  # False. It's titlelized version is "Python"
numbers = (1, 2, 3, 4)print(numbers[-1])  # 4print(numbers[-4])  # 1
mixed_tuple = (("a"*10, 3, 4), ['first', 'second', 'third'])print(mixed_tuple[1])  # ['first', 'second', 'third']print(mixed_tuple[0])  # ('aaaaaaaaaa', 3, 4)
names = ["Besim", "Albert", "Besim", "Fisnik", "Meriton"]print(names.count("Besim"))  # 2
my_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]slicing = slice(-4, None)# Getting the last 3 elements from the listprint(my_list[slicing])  # [4, 5, 6]# Getting only the third element starting from the rightprint(my_list[-3])  # 4
You can also use slice() for other usual slicing tasks, like:
string = "Data Science"# start = 1, stop = None (don't stop anywhere), step = 1# contains 1, 3 and 5 indicesslice_object = slice(5, None)print(string[slice_object])   # Science
my_tuple = ('a', 1, 'f', 'a', 5, 'a')print(my_tuple.count('a'))  # 3
my_tuple = ('a', 1, 'f', 'a', 5, 'a')print(my_tuple.index('f'))  #  2
my_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)print(my_tuple[::3])  # (1, 4, 7, 10)
my_tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)print(my_tuple[3:])  # (4, 5, 6, 7, 8, 9, 10)
my_list = [1, 2, 3, 4]my_list.clear()print(my_list)  # []my_set = {1, 2, 3}my_set.clear()print(my_set)  # set()my_dict = {"a": 1, "b": 2}my_dict.clear()print(my_dict)  # {}
One way is to use the method union() which returns a new set as a result of the joining:
first_set = {4, 5, 6}second_set = {1, 2, 3}print(first_set.union(second_set))  # {1, 2, 3, 4, 5, 6}
Another one is method update, which inserts the element of the second set into the first one:
first_set = {4, 5, 6}second_set = {1, 2, 3}first_set.update(second_set)print(first_set)  # {1, 2, 3, 4, 5, 6}
def is_positive(number):    print("Positive" if number > 0 else "Negative")  # Positiveis_positive(-3)
math_points = 51biology_points = 78physics_points = 56history_points = 72my_conditions = [math_points > 50, biology_points > 50,                 physics_points > 50, history_points > 50]if all(my_conditions):    print("Congratulations! You have passed all of the exams.")else:    print("I am sorry, but it seems that you have to repeat at least one exam.")# Congratulations! You have passed all of the exams.
math_points = 51biology_points = 78physics_points = 56history_points = 72my_conditions = [math_points > 50, biology_points > 50,                 physics_points > 50, history_points > 50]if any(my_conditions):    print("Congratulations! You have passed all of the exams.")else:    print("I am sorry, but it seems that you have to repeat at least one exam.")# Congratulations! You have passed all of the exams.
print(bool("Non empty"))  # Trueprint(bool(""))  # False
print(bool([]))  # Falseprint(bool(set([])))  # Falseprint(bool({}))  # Falseprint(bool({"a": 1}))  # True
print(bool(False))  # Falseprint(bool(None))  # Falseprint(bool(0))  # False
string = "string"def do_nothing():  string = "inside a method"do_nothing()print(string)  # string
You need to use the access modifier global as well:
string = "string"def do_nothing():    global string    string = "inside a method"do_nothing()print(string)  # inside a method
from collections import Counterresult = Counter("Banana")print(result)  # Counter({'a': 3, 'n': 2, 'B': 1})result = Counter([1, 2, 1, 3, 1, 4, 1, 5, 1, 6])print(result)  # Counter({1: 5, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1})
from collections import Counterdef check_if_anagram(first_string, second_string):    first_string = first_string.lower()    second_string = second_string.lower()    return Counter(first_string) == Counter(second_string)print(check_if_anagram('testinG', 'Testing'))  # Trueprint(check_if_anagram('Here', 'Rehe'))  # Trueprint(check_if_anagram('Know', 'Now'))  # False
You can also check whether 2 strings are anagrams using sorted():
def check_if_anagram(first_word, second_word):    first_word = first_word.lower()    second_word = second_word.lower()    return sorted(first_word) == sorted(second_word)print(check_if_anagram("testinG", "Testing"))  # Trueprint(check_if_anagram("Here", "Rehe"))  # Trueprint(check_if_anagram("Know", "Now"))  # False
from itertools import countmy_vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']current_counter = count()string = "This is just a sentence."for i in string:    if i in my_vowels:        print(f"Current vowel: {i}")        print(f"Number of vowels found so far: {next(current_counter)}")
This is the result in the console:
Current vowel: iNumber of vowels found so far: 0Current vowel: iNumber of vowels found so far: 1Current vowel: uNumber of vowels found so far: 2Current vowel: aNumber of vowels found so far: 3Current vowel: eNumber of vowels found so far: 4Current vowel: eNumber of vowels found so far: 5Current vowel: eNumber of vowels found so far: 6
Counter from the collections module by default doesn’t order elements based on their frequencies.
from collections import Counterresult = Counter([1, 2, 3, 2, 2, 2, 2])print(result)  # Counter({2: 5, 1: 1, 3: 1})print(result.most_common())  # [(2, 5), (1, 1), (3, 1)]
my_list = ['1', 1, 0, 'a', 'b', 2, 'a', 'c', 'a']print(max(set(my_list), key=my_list.count))  # a
Here is the explanation from the docs:
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
Maybe, an even more comprehensive description can be found here:
A shallow copy means constructing a new collection object and then populating it with references to the child objects found in the original. In essence, a shallow copy is only one level deep. The copying process does not recurse and therefore won’t create copies of the child objects themselves.A deep copy makes the copying process recursive. It means first constructing a new collection object and then recursively populating it with copies of the child objects found in the original. Copying an object this way walks the whole object tree to create a fully independent clone of the original object and all of its children.
Here is an example for the copy():
first_list = [[1, 2, 3], ['a', 'b', 'c']]second_list = first_list.copy()first_list[0][2] = 831print(first_list)  # [[1, 2, 831], ['a', 'b', 'c']]print(second_list)  # [[1, 2, 831], ['a', 'b', 'c']]
Here is an example for the deepcopy() case:
import copyfirst_list = [[1, 2, 3], ['a', 'b', 'c']]second_list = copy.deepcopy(first_list)first_list[0][2] = 831print(first_list)  # [[1, 2, 831], ['a', 'b', 'c']]print(second_list)  # [[1, 2, 3], ['a', 'b', 'c']]
If you use a usual dictionary and try to access a non-existent key, then you are going to get an error:
my_dictonary = {"name": "Name", "surname": "Surname"}print(my_dictonary["age"])  
Here it’s the error thrown:
KeyError: 'age'
We can avoid such errors using defaultdict():
from collections import defaultdictmy_dictonary = defaultdict(str)my_dictonary['name'] = "Name"my_dictonary['surname'] = "Surname"print(my_dictonary["age"])  
class OddNumbers:    def __iter__(self):        self.a = 1        return self    def __next__(self):        x = self.a        self.a += 2        return xodd_numbers_object = OddNumbers()iterator = iter(odd_numbers_object)print(next(iterator))  # 1print(next(iterator))  # 3print(next(iterator))  # 5
my_set = set([1, 2, 1, 2, 3, 4, 5])print(list(my_set))  # [1, 2, 3, 4, 5]
import torchprint(torch)  # <module 'torch' from '/Users/...'
odd_numbers = [1, 3, 5, 7, 9]even_numbers = []for i in range(9):    if i not in odd_numbers:        even_numbers.append(i)print(even_numbers)  # [0, 2, 4, 6, 8]
sort() sorts the original list.
sorted() returns a new sorted list.
groceries = ['milk', 'bread', 'tea']new_groceries = sorted(groceries)# new_groceries = ['bread', 'milk', 'tea']print(new_groceries)# groceries = ['milk', 'bread', 'tea']print(groceries)groceries.sort()# groceries = ['bread', 'milk', 'tea']print(groceries)
UUID stands for Universally Unique Identifier.
import uuid# Generate a UUID from a host ID, sequence number, and the current timeprint(uuid.uuid1())  # 308490b6-afe4-11eb-95f7-0c4de9a0c5af# Generate a random UUIDprint(uuid.uuid4())  # 93bc700b-253e-4081-a358-24b60591076a
If you come from a Java background, you know that String in Java is a non-primitive data type, because it refers to an object.
In Python, a string is a primitive data type.
I hope you learned at least something from this article.