>>> from pathlib import Path
>>>
>>> dir = Path("/home/aric/INST326")
>>> script = Path("/home/aric/INST326/homework1.py")
All values in Python are objects
All objects are instances of a data type
Objects can have properties
Methods (functionality)
Attributes (data)
Classes define data types
Classes allow us to combine data and functionality into a single package
Classes are used to create (or instantiate) objects
Classes let us define how operators and other features of Python behave for the objects we create
int
str
float
list
bool
mylist.append("some value")
mystring.upper()
myfile.close()
pathlib.Path
The Path
class from pathlib
is an excellent example of a good class.
We’ll look at
instantiation
some attributes
some methods
interaction with features of Python
To create an instance of a class, invoke the class like a function: cls()
Arguments (if any) go inside the parentheses: cls(
arg1,
arg2,
…)
When creating a Path
object, you can specify a path as a string
>>> from pathlib import Path
>>>
>>> dir = Path("/home/aric/INST326")
>>> script = Path("/home/aric/INST326/homework1.py")
Attributes are accessed like this: obj.
attribute
Path
object attributes include
name
: the name of the file, without the directory
suffix
: the file extension, if any
parent
: the directory of the file, as a Path
object
>>> script.name
'homework1.py'
>>> script.suffix
'.py'
>>> script.parent
PosixPath('/home/aric/INST326')
Methods are invoked like this: obj.
method()
Path
object methods include
exists()
: indicates whether or not the file exists
read_text()
: open the file as a text file and return the contents
glob()
: search for files in a directory matching a pattern
Arguments (if any) go inside the parentheses: obj.
method(
arg1,
arg2,
…)
>>> script.exists()
True
>>> script.read_text(encoding="utf-8")
'print("Hello world!")\n'
>>> for path in dir.glob("*.py"):
... print(path)
...
/home/aric/INST326/exercise1.py
/home/aric/INST326/homework1.py
Using str()
on a Path
object gives you the path as a string:
>>> str(dir)
'/home/aric/INST326'
Using the /
operator on a Path
object lets you create child paths:
>>> dir / "homework2.py"
PosixPath('/home/aric/INST326/homework2.py')
Using pathlib.Path
, we looked at examples of
how to instantiate a class
how to access attributes of an object
how to invoke methods of an object
how objects can define how they interact with features of Python
Classes are defined using a class
statement
By convention, non-built-in class names use CamelCase
class
ClassName:
body
class Blob:
pass
pass
statementpass
is called a no-op: it does nothing
pass
is used when we need a compound statement, but we have nothing meaningful to put in the body
Remember, all compound statements need a body
pass
statementInstead of this:
if today == user_birthday:
print("Happy birthday!")
else:
pass
Do this:
if today == user_birthday:
print("Happy birthday!")
class Blob:
pass
Instantiating the class
myblob = Blob()
Setting attributes on our instance
myblob.name = "Desdemona"
myblob.color = "purple"
Accessing our instance’s attributes
print(myblob.name)
print(myblob.color)
Attributes let us associate data with an object
We can use that data inside the object’s methods as well as outside the object
Like global variables, attributes persist beyond the lifetime of a method call
Unlike global variables, attributes live in a specific object’s namespace (so they’re less likely to be accidentally modified)
When you are tempted to use a global variable, consider writing a class and using an attribute instead
Classes are declared using class
statements
The simplest class we can write has a pass
statement for a body and inherits all its functionality from Python
To make an instance of a class, we call the class like a function
Attributes are variables in the namespace of a class instance
Attributes are often a good alternative to global variables
Methods are basically functions declared inside of a class
Most methods require a special parameter called self
as their first parameter
class Greeter:
def greet(self):
print("Hello!")
>>> g = Greeter()
>>> h = Greeter()
>>> g.greet()
Hello!
>>> h.greet()
Hello!
Names in Python are references to objects
It’s possible for more than one name to refer to the same object in Python
>>> b = myblob
>>> b.name
'Desdemona'
>>> b.color
'purple'
>>> b is myblob
True
self
self
is a reference to whichever class instance is running your code at a given point in time
If you have a Greeter
object named g
and you call g.greet()
, self
is a reference to g
self
demonstration>>> class Greeter:
... def greet(self):
... print(f"Hi, my name is {self.name}!")
...
>>> g = Greeter()
>>> g.name = "Gabrielle"
>>> h = Greeter()
>>> h.name = "Henry"
>>> g.greet()
Hi, my name is Gabrielle!
>>> h.greet()
Hi, my name is Henry!
self
is how you access an object’s attributes and methods within the object’s methods
Methods are basically functions declared inside a class
self
is a (mostly) required parameter that refers to the object that is running your code
self
gives you access to an object’s attributes and methods within the object’s methods
__init__()
methodWe often want to set some initial attributes on new objects
Less commonly, we may want new objects to perform some action upon creation
We can define a "magic" method called __init__()
to initialize a new object
If we want a class to take arguments when we instantiate it, __init__()
is where we declare these parameters
__init__()
exampleclass Greeter:
def __init__(self, name):
self.name = name
def greet(self):
print(f"Hi, my name is {self.name}!")
__init__()
: important pointsJust because something is a parameter of __init__()
doesn’t mean we have to make an attribute for it
We can create attributes that don’t correspond to parameters of __init__()
__init__()
is a "magic" method that lets us set attributes and perform other actions when a new object is created
__init__()
is where we define parameters that should be specified when the class is instantiated
Not all parameters of __init__()
have to correspond to attributes
Not all attributes have to correspond to parameters of __init__()
Docstrings give other programmers enough information to use our code
Three kinds:
Function/method docstrings
Class docstrings
Module docstrings
Docstrings are statements, not comments
Docstrings must be the first statement in the body of the thing they are describing
>>> def myfunction():
... """This shows that docstrings are special."""
... pass
...
>>> help(myfunction)
Help on function myfunction in module __main__:
myfunction()
This shows that docstrings are special.
>>> myfunction.__doc__
'This shows that docstrings are special.'
def myfunction():
... pass
... """This docstring is in the wrong place."""
...
>>> help(myfunction)
Help on function myfunction in module __main__:
myfunction()
>>> myfunction.__doc__
>>>
Class docstrings should
begin with a brief description of the purpose of the class
document the data type and meaning of each attribute
class Greeter:
""" An object that gives users a friendly greeting.
Attributes:
name (str): the greeter's name.
"""
Docstrings document how to use our code
Functions/methods, classes, and modules can have docstrings
Improperly located docstrings aren’t docstrings
Class docstrings explain
the purpose of the class
the data type and meaning of each attribute
class Memobox:
""" A mechanism for users to communicate with each other.
Attributes:
name (str): the user's name.
contacts (dict of str: Memobox): the people the user
communicates with and their Memobox objects. Each key is the
name of a contact; each value is that contact's Memobox.
memos (list of (tuple of str, str)): unread memos received
by the user. Each memo is a tuple consisting of a sender
and a memo string.
"""
def __init__(self, name):
""" Initialize new Memobox object.
Side effects:
Sets attributes name, contacts, and memos.
"""
self.name = name
self.contacts = dict()
self.memos = list()
def add_contact(self, contact):
""" Add contact to contacts.
Args:
contact (Memobox): Memobox of a contact to add.
Side effects:
Modifies contacts attribute.
"""
self.contacts[contact.name] = contact
def receive_memo(self, sender, memo):
""" Receive a memo.
Args:
sender (Memobox): the sender of the memo.
memo (str): the memo that was received.
Side effects:
Modifies memos attribute.
"""
self.memos.append((sender.name, memo))
def send_memo(self, recipient, memo):
""" Send a memo.
Args:
recipient (str): the intended recipeint of the memo.
memo (str): the memo to send.
Side effects:
Invokes recipient's receive_memo() method.
Raises:
ValueError: recipient is not listed in contacts.
"""
if recipient not in self.contacts:
raise ValueError(f"recipient {recipient} not in contacts")
self.contacts[recipient].receive_memo(self, memo)
def read_memos(self):
""" Display all memos and clear memo queue.
Side effects:
Modifies memos attribute.
Writes to stdout.
"""
if len(self.memos) > 0:
for sender, memo in self.memos:
print(f"memo from {sender}: {memo}")
self.memos = list()
else:
print("No new memos")