diff --git a/Programming_Languages/Python/BBC_Course_Notes.md b/Programming_Languages/Python/BBC_Course_Notes.md deleted file mode 100644 index 898f2bc..0000000 --- a/Programming_Languages/Python/BBC_Course_Notes.md +++ /dev/null @@ -1,17 +0,0 @@ -# BBC Python Course notes - -## Day 2 - -With lists you have to use copy if you wish to make a new version. You cannot just reassign to a new version. This will still update the original. Since it copies the pointer. - -Distinguish functions that will create new list and methods which will modify existing list - -Functions: named parameter passing, use for default parameter values - -Python does not have constants but has a convention of upper case to mimic constants - -More on addresses and pointers in Python - -With classes we don't need to use `new` when instantiating an instance of a class. - -You do not need to define properties in classes if they exist in the constructor diff --git a/Programming_Languages/Python/Concepts/Python_modules.md b/Programming_Languages/Python/Concepts/Python_modules.md new file mode 100644 index 0000000..eab4b53 --- /dev/null +++ b/Programming_Languages/Python/Concepts/Python_modules.md @@ -0,0 +1,106 @@ +--- +categories: + - Programming Languages +tags: [python, OOP] +--- + +# Python modules + +Modules in Python are very similar to modules in Node.js: a mix of several pieces of functionality (often written in a mix of syntactic styles) bundled together to be used as a unit. + +## Creating modules + +### Naming modules + +The name of a module is the name of its file, e.g. `utils.py` defines the module `utils`. When we import a module we do not need to specify the extension. + +### Example module + +Here is an example of a module. We will use this throughout. + +```py +"""utils module""" + +print('Hello I am the utils module') + + +def printer(some_object): + print('printer') + print(some_object) + print('done') + + +class Shape: + + def __init__(self, id): + self.id = id + + def __str__(self): + return 'Shape - ' + self.id + + +default_shape = Shape('square') + + +def _special_function(): + print('Special function') + +``` + +## Importing modules + +To use the module defined in `utils.py`: + +### Method one + +```py +import utils +``` + +Now we can use the functionality provided by the module, e.g: + +```py +import utils + +utils.printer(utils.default_shape) + +shape = utils.Shape("circle") +``` + +Here we have invoked the `printer()` function contained in the module. We have used it to print the `default_shape` variable in `utils` which is itself and instance of the `Shape` class, also defined in `utils`. + +After this we have created a new instance of the `Shape` class in the file that imports `utils`. + +### Method two + +In the last example we used the name `utils` to reference our import. I prefer this because it shows the origin of the code you are using. But if we imported like so: + +```py +from utils import * +``` + +Then we wouldn't need to prepend our use of `utils` functions with `utils`. Instead out code would look like this: + +```py +from utils import * + +printer(default_shape) + +shape =Shape("circle") +``` + +### Importing a subset of the module + +It doesn't look like you need to explicity export the values you want to be able to use once your module has been imported in Python. Everything seems to be exported and accessible. + +However, you can choose to import only a subset of the module: + +```py +from utils import Shape +``` + +And you can use an alias, e.g: + +```py +from util import Shape as banana +``` diff --git a/Programming_Languages/Python/Syntax/Class_inheritance_in_Python.md b/Programming_Languages/Python/Syntax/Class_inheritance_in_Python.md new file mode 100644 index 0000000..f7a79fd --- /dev/null +++ b/Programming_Languages/Python/Syntax/Class_inheritance_in_Python.md @@ -0,0 +1,68 @@ +--- +categories: + - Programming Languages +tags: [python, OOP] +--- + +# Class inheritance in Python + +We distinguish the **parent/source/superclass** class and the **subclass** that **extends** it. + +A subclass can have more than one parent class that it extends, but this is atypical. + +> The subclass will inherit all attributes and methods of the superclass unless it overrides them. + +In addition to overriding the methods of the parent we can extend them in the child. This is a bit like using an abstract. + +## Example of class inheritance + +```py + +class Person: + """ Our superclass + """ + def __init__(self, name, age): + self.name = name + self.age = age + + def birthday(self): + print('Happy birthday you were', self.age) + self.age += 1 + print('You are now', self.age) + + def __str__(self): + return self.name + ' is ' + str(self.age) + + +class Employee(Person): + """ Our subclass that extends `Person` + """ + def __init__(self, name, age, id): + super().__init__(name, age) + self.id = id + + def calculate_pay(self, hours_worked): + rate_of_pay = 7.50 + if self.age >= 21: + rate_of_pay += 2.50 + return hours_worked * rate_of_pay + + def __str__(self): + return super().__str__() + ' - id(' + str(self.id) + ')' +``` + +### Key points + +- We pass the name of the parent class to the child class as a parameter +- We must include all of the parent class's attributes in the constructor of the subclass +- We must use a special `super().__init__(..,...)` method to enact the extension of the parent. This method references the `__init__` class in the parent. + +## Protected/private methods and attributes + +> Protected methods and attributes are private to the class but can be accessed from any subclass + +> Private methods cannot be accessed from anywhere outside of the class + +We declare private methods and attributes with a double underscore (`__`). We declare protected methods and attributes with a single underscore (`_`). + +This is **convention**, there are not actually private and protected attributes or properties. diff --git a/Programming_Languages/Python/Syntax/Classes_in_Python.md b/Programming_Languages/Python/Syntax/Classes_in_Python.md new file mode 100644 index 0000000..9d67aa6 --- /dev/null +++ b/Programming_Languages/Python/Syntax/Classes_in_Python.md @@ -0,0 +1,138 @@ +--- +categories: + - Programming Languages +tags: [python, OOP] +--- + +# Classes in Python + +## General points + +- We don't need to use `new` when instantiating an instance of a class. +- All properties and methods must be defined in the constructor. This is in contrast to JavaScript where the properties in the constructor are those we want to initialise when the object is created and where there may be other properties and methods that are used outside of the instantiation process. +- In contrast to TypeScript and other OOP languages it is not necessary to declare the properties you wish to define in the constructor, outside of the constructor. As long as they are defined in the constructor they are accessible. + +## Basic syntax + +```py +class Person: + """ An example class to hold a persons name and age""" + + def __init__(self, name, age): + self.name = name + self.age = age + + def birthday(self): + print('Happy birthday you were', self.age) + self.age += 1 + print('You are now', self.age) + +p1 = Person('John', 36) +p2 = Person('Thomas', 34) +print(p1) +print(p2) + +print(p1.name) + +# John +``` + +Key points to note: + +- The `__init__` method is the constructor function and must exist on every class to define the properties of the class +- `self` is a reference to the class itself and the object it will create, akin to `this` in other languages +- You must pass `self` as a parameter to every method (this is a difference from JS) +- As with functions, we can use docstrings to document the class. What you write here will show up in Intellisense etc + +## More complex class + +```py +class Person: + """ An example class to hold a persons name and age""" + + def __init__(self, name, age): + self.name = name + self.age = age + + def __str__(self): + return self.name + ' is ' + str(self.age) + + def birthday(self): + print ('Happy birthday you were', self.age) + self.age += 1 + print('You are now', self.age) + + def calculate_pay(self, hours_worked): + rate_of_pay = 7.50 + if self.age >= 21: + rate_of_pay += 2.50 + return hours_worked * rate_of_pay + + def is_teenager(self): + return self.age < 20 +``` + +## Object references + +When you log a class you get a reference to its hexadecimal [memory](/Computer_Architecture/Memory/Memory.md) reference. + +```py +p1 = Person('John', 36) +p2 = Person('Thomas', 34) + +print(p1) +print(p2) + +# <__main__.Person object at 0x102e75510> +# <__main__.Person object at 0x102e75511> +``` + +This shows each object is unique. You can also generate a specific ID with the `id()` method: + +```py +print(id(p1)) +print(id(p2)) + +# 4379088272 +# 4379088656 +``` + +## Copying objects + +The same principle that applies to [copying functions](/Programming_Languages/Python/Syntax/Functions_in_Python.md) applies to copying objects created through classes: redeclaration results in a duplicate entity. Thus changes to the duplicate will affect the original. + +This becomes obvious when you use `id()` but otherwise might not be apparent. To copy you should therefore declare a new instance of the class. + +### The `str` method + +The memory reference isn't very helpful for viewing the data contents. To get around this we can use the builtin `__string__` method which automatically logs whatever properties you put in there: + +```py +class Person: + """ An example class to hold a persons name and age""" + + def __init__(self, name, age): + self.name = name + self.age = age + + def __str__(self): + return self.name + ' is ' + str(self.age) + +p3 = Person('Thomas', 34) +print(p3) +# Thomas is 34 +``` + +## Deleting objects + +You might want to delete an object reference because: + +- the variable referencing the object goes out of scope +- the variable is set to `None` + +After the `del` statement is applied to a variable that holds an object, the object will no longer be available and any attempt to reference it will result in an error. + +```py +p1 = Person('J-Man', 76) +del p1 +``` diff --git a/Programming_Languages/Python/Syntax/Lambdas_in_Python.md b/Programming_Languages/Python/Syntax/Lambdas_in_Python.md index a849594..a2dbf0e 100644 --- a/Programming_Languages/Python/Syntax/Lambdas_in_Python.md +++ b/Programming_Languages/Python/Syntax/Lambdas_in_Python.md @@ -15,10 +15,14 @@ They are most often used unnamed with the functional methods [map, filter and re Here is the two syntaxes side by side: ```js +// JavaScript + const double = (x) => x * x; ``` ```py +# Python + double = lambda x: x * x ``` diff --git a/Programming_Languages/Python/Syntax/Lists_in_Python.md b/Programming_Languages/Python/Syntax/Lists_in_Python.md index 2bfe69b..c552482 100644 --- a/Programming_Languages/Python/Syntax/Lists_in_Python.md +++ b/Programming_Languages/Python/Syntax/Lists_in_Python.md @@ -18,6 +18,14 @@ Lists have the following properties: > Lists are denoted with `[...]` +## Duplicating lists + +When we want to duplicate a list, we can't just reassign the list to a new variable and expect this to be a copy. + +If we edit this "copy" it will update the original list since it copies the pointer and will therefore point to the same address in memory. Instead we have to use the List `copy()` function which returns a new list and doesn't modify the original list. + +Relatedly, we should distinguish those List methods and functions which create a new list (non-destructive) and those that mutate the existing list. + ## Basic usage ```python diff --git a/Programming_Languages/Python/Syntax/Naming_conventions.md b/Programming_Languages/Python/Syntax/Naming_conventions.md index 6273ed6..5303c24 100644 --- a/Programming_Languages/Python/Syntax/Naming_conventions.md +++ b/Programming_Languages/Python/Syntax/Naming_conventions.md @@ -15,3 +15,7 @@ To name a variable or method with multiple words we use underscores to separate an_int = 32 print(an_integer.is_integer) # true ``` + +## Constants + +There are no constants in Python. However there a pseudo-constants established by convention. To denote that a variable should not change you use `UPPER_CASE`. diff --git a/_meta/Topic_Log.md b/_meta/Topic_Log.md index 6c8915f..9330da0 100644 --- a/_meta/Topic_Log.md +++ b/_meta/Topic_Log.md @@ -15,10 +15,10 @@ BBC Course, remaining topics: - - Classes and object-oriented paradigms in Python - - Modules + - Entrypoint modules - Error handling - Testing + - I/O ## Bash @@ -59,6 +59,8 @@ ## NodeJS - Build examples of read stream and write stream +- Can you override parent class methods in JS subclass? + - If not in JS, can you do this in TS? ## Git