int
float
str
bool
None
list
dict
set
tuple
*Up until this point, you have been introduced to data types as they grew in the woods and just exist as a fact of the universe.
This is actually not the case. Data types are something that programmers create to organize the way that their code is used.
Let's say we're building a Venmo-like app and we need some way to represent a transfer of money in our code. There are 4 attributes that represent a transfer:
str
)???
)???
)???
)str
)str
)Decimal
)datetime
)What will this expression evaluate to?
1.2 - 1.0
Tuple[str, str, Decimal, datetime]
t1 = ("Sally", "Bob", Decimal("25.50"), datetime(2020, 8, 5))
t2 = ("Bob", "Fran", Decimal("15.20"), datetime(2020, 8, 6))
t3 = ("Bob", "Matt", Decimal("34.29"), datetime(2020, 8, 26))
t4 = ("Matt", "Bob", Decimal("34.29"), datetime(2020, 9, 1))
sender = transfer[0]
receiver = transfer[1]
amount = transfer[2]
date = transfer[3]
# Or use tuple unpacking...
sender, receiver, amount, date = transfer
transfer
type:transfer.append([1, 2, 3])
will error
transfer = ("Bob", "Sally", Decimal(35.50), datetime.now())
new_transfer = transfer[:2] + (Decimal(36.50), ) + transfer[3:]
t1 = {"from": "Sally",
"to": "Bob",
"amount": Decimal("25.50"),
"date": datetime(2020, 8, 5)
}
t2 = {"from": "Bob",
"to": "Fran",
"amount": Decimal("15.20"),
"date": datetime(2020, 8, 6)
}
transfer
type:t2["invalid_property"] = True
t1 = Transfer("Bob", "Sally", Decimal(35.50), datetime(2020, 10, 1))
# alternative method, with keyword arguments
t1 = Transfer(sender="Bob",
receiver="Sally",
amount=Decimal(35.50),
date=datetime(2020, 10, 1))
t1.sender # "Bob"
t1.date # datetime(2020, 10, 1)
# Assignment update the value!
t1.amount = Decimal(36.50)
t1.amount # Is now Decimal(36.50)
Classes act a little bit like dictionaries:
t1 = Transfer(sender="Bob",
receiver="Sally",
amount=Decimal(35.50),
date=datetime(2020, 10, 1))
# Gives you "Bob"
t1.sender
t2 = {"sender": "Bob",
"receiver": "Sally",
"amount": Decimal(35.50),
"date": datetime(2020, 10, 1)}
# Also gives you "Bob"
t2["sender"]
If you squint hard, Python's tuples and classes and dictionaries are the same kind of thing. In type theory, this is called a product type.
\[\begin{aligned} str \times str \times \Bbb{Q} \times date \end{aligned} \]The product of all possible strings, all possible strings, all possible rationals, and all possible dates makes up the domain of this type.
In cases where you have a collection of heterogeneous values that have meaning together, a tuple or a class or a dictionary is useful.
The semantics may vary:
# With tuple:
transfer[0]
# With dictionary:
transfer["sender"]
# With class:
transfer.sender
...but each of these give us the same thing: the username of the person that sent the transfer.
Think of classes as tuples/dictionaries+++.
Minimal, complete definition of our transfer type as a class:
class Transfer:
def __init__(self, sender, receiver, amount, date):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
Given:
class Transfer:
def __init__(self, sender, receiver, amount, date):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
We construct a transfer this way:
t1 = Transfer("Bob", "Matt", Decimal(5), datetime(2020, 2, 1))
Given:
class Transfer:
def __init__(self, sender, receiver, amount, date):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
t1 = Transfer("Bob", "Matt", Decimal(5), datetime(2020, 2, 1))
Transfer
is a classt1
is an object that is an instance of a Transfer
The class is not the thing. It's a blueprint for making the thing.
Or making many things.
class Transfer:
def __init__(self, sender, receiver, amount, date):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
init
as in initialize.__init__
function is inside the class and belongs to it.
Transfer("Bob", "Matt", Decimal(5), datetime(2020, 2, 1))
__
are magicWhat is this self
thing about?
class Transfer:
def __init__(self, sender, receiver, amount, date):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
self
.self
refers to the object itself that has been constructed from the class.You can write your own methods too!
class Transfer:
def __init__(self, sender, receiver, amount, date):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
def days_ago(self):
time_delta = datetime.now() - self.date
return time_delta.days
def apply_processing_fee(self):
self.amount = self.amount + Decimal("0.25")
We can make this a bit better...
class Transfer:
def __init__(self, sender, receiver, amount, date=datetime.now()):
self.sender = sender
self.receiver = receiver
self.amount = amount
self.date = date
self.processing_fee_applied = False
def days_ago(self):
time_delta = datetime.now() - self.date
return time_delta.days
def apply_processing_fee(self, fee=Decimal("0.25")):
self.amount = self.amount + fee
self.processing_fee_applied = True
Using our class:
t1 = Transfer("Bob", "Sally", Decimal(35.50), datetime(2019, 10, 1))
t2 = Transfer(sender="Sally",
receiver="Matt",
amount=Decimal(5.25))
# This will be "Sally":
t1.receiver
# This will be True:
t1.date < t2.date
# This will be 0:
t2.days_ago()
t1.apply_processing_fee():
# This will be $35.50 + $0.25 = $35.75:
t1.amount
The next time we discuss classes, we'll do so in the context of Object Oriented Programming (OOP). OOP is a style of programming where the vast majority of processing takes place as the interaction between objects and their methods.