Hello!
Here marks another phase completed with #flatironschool! I feel as though this phase (phase 3) was quite a different approach to the learning process.
First off - this phase was done in a new language, Python!
Now... At first (And possibly even still...) It's not my favorite of the languages I've been introduced to. That being said - I adore object oriented programming and I thought I'd share some of what I've learned.
But, before we dive into Object-oriented programming - let's mark out something I have enjoyed about Python.
It's clear and concise in its language! If I wanted to consume every cookie in the cookie jar, it's as easy as looping through it like so:
for cookie in cookie_jar:
consume(cookie)
That same could be done in javascript but would look something like...
cookie_jar.forEach((cookie) => {consume(cookie)};
// or
for (let cookie of cookie_jar) { consume(cookie);
The javascript is far less readable out the box.
This is just one example.
Printing to the console is straightforward in both. But Python just reads more easily.
print("foo")
console.log("foo"
Checking the length of an array or string makes more sense to be a wrapper around the object rather than a function OF the object.
len(["1","2","3"])
["1","2","3"].length()
But I enjoy most of all - classes.
Let's look at an example of a class and break it down line by line.
Person.py
1. # lib/person/Person.py
2. from lib.person._Person import PersonMethods
3.
4. class Person(PersonMethods):
5.
6. def __init__(self,name):
7. self.name = name
8.
9. @property
10. def name(self):
11. return self._name
12.
13. @name.setter
14. def name(self, name):
15. if isinstance(name, str) and len(name):
16. self._name = name
17. else:
18. raise ValueError(
19. "Name must be a non-empty string"
20. )
Breakdown
Line 1: A Single line comment indicating the file path.
Line 2: from lib.person._Person import PersonMethods
Imports the PersonMethods
class from the _Player
module.
Line 4: class Person(PersonMethods):
Defines a new class named Person
that inherits from the PersonMethods
class imported on Line 2. Inheritance allows a class to inherit attributes and methods from another class. In this case, separating methods from Getters / Setters.
Line 6: __init__
is a Constructor
. This is a special method that is called when a new instance of the class is instantiated. There are two parameters to the constructor here, self
and name
. self
refers to the instance of the class and is not expressly passed into the constructor upon creation. name
is a string (in this case) that is passed into the constructor when initializing the instance.
Line 7: self.name = name
This line assigns the value of name
that was passed in on line 6 into an attribute of the current instance self
.
Line 9 - 11: @property
is a decorator
used to define a property. Properties provide a way to customize attribute access. For example:
person = Person("John")
print(person.name) # This will print "John"
However this is equivalent to calling the name()
method on the instance.
print(person.name()) # This will also print "John"
By using the @property
decorator, it allows us to more cleanly access the attribute as though it were a direct attribute.
Line 13: @name.setter
is a decorator that defines a setter method for the name
property.
Line 14: def name(self, name):
defines the name
method as a setter based on the decorator in line 13.
Line 15: if isinstance(name, str) and len(name):
This is our first bit of logic! This line checks if the value passed into the setter, name
, is a non-empty string. isinstance()
is a function to check if the value is an instance of the string
class and the len()
function checks if the string has a length greater than 0
Line 17 - 20: else: raise ValueError("Name....")
this line only fires if the value passed into the setter method is not a valid string. It will raise a ValueError
with the message attached.
All in all, by defining a setter method for the name property, the name
attribute of an instance Person
can be modified using assignment syntax. For example:
person = Person("John")
person.name = "Alice" # Changes the name of person
_Person.py
1. # lib/person/_Person.py
2.
3. class PersonMethods:
4.
5. def say_name(self):
6. return f"My name is {self.name}
Breakdown
Line 1: A Single line comment indicating the file path.
Line 3: class PersonMethods:
defines a new class PersonMethods
that, unlike Person
, does not inherit any methods. Instead, this class has a single method say_name()
.
Line 5: def say_name(self):
definds a method named say_name
within the method that has the self
parameter, indicating that this is an instance method (a method specifically used by each instance of the class rather than the class itself).
Line 6: return f"My name is {self.name}"
returns a string formatted using string interpolation.
All together now!
So! By inheriting from PersonMethods
, instances of the Person
class gain access to the methods defined in the PersonMethods
class! For example, the say_name()
method we created earlier can be invoked directly on instances of the Person
class!
person = Person("John")
print(person.say_name()) # Output: "My name is John"
This illustrates how the Person
class in Person.py
benefits from the methods encapsulated in the PersonMethods
class defined in _Person.py
!
A Caveat to Python...
While I love the object oriented programming paradigm... Python has a major annoyance for my organized brain...
The indentation! 4 spaces!? With Javascript, just wrap everything in curly braces {}. Boom! You know where your scope lines are.
This whitespace sensitivity to define block structure is a real visual problem for me. I appreciate the explicit block delimiters that javascript has with {}
.
I learned a lot more about Python but this is just a neat little explanation of something I found fun to learn.
Top comments (0)