19
loading...
This website collects cookies to deliver better user experience
object
.id
.# Given an object identified by the variable name `a`
>>> a = 1
# We can access its memory location `id`
>>> id(a)
1407854654624
# We can access its type
>>> type(a)
'<class 'int'>'
# We can access its value
>>> a
1
# We can use the behavior defined on its protocol
>>> a + 1
2
>>> a.hex()
'0x1'
class
definition.print
<- is function
type"Hello"
<- is str
type0
<- is int
type[0:5]
<- is a slice
typestr.upper
<- is a method_descriptor
type
Function String Integer Slice
__⬆️__ __⬆️__ _⬆️___ __⬆️__
print("Hello"[0] + "World!"[0:5].upper())
___⬆️___ __⬆️___
Symbol Method Descriptor
Python is a POP Language (Protocol Oriented Programming)
Callable Subscriptable Sliceable
__⬆️__ ______⬆️__ __________⬆️__
print("Hello"[0] + "World!"[0:5].upper())
________⬆️________ __⬆️__
Summable Callable
Callable 🗣️ Can be invoked using ()
A type is also callable when its protocol includes the __call__
method.
Subscriptable ✍🏻 its elements can be accessed through a subscription.
The subscription can be numeric ordinal [0]
or named key ['name']
. A type is Subscriptable when its protocol includes the __getitem__
method.
Sliceable 🔪 its collection of elements can be sliced in parts.
A type is Sliceable when it is Subscriptable and its __getitem__
method can accept a slice
in place of the index
or name
. A slice is the composition of [start:stop:step]
elements.
Summable ➕ Can be combined with other objects via +
operation.
The product of this combination is always new object. On numeric types this is the ability to sum
two or more numbers in a set. On sequences it is the concatenation
of its fragments in to one. A type is Summable when its protocol includes the __add__
or __radd__
method.
Printable 🖨️ Can be printed using print
All the Python objects are printable, print
will look either for a __repr__
or a __str__
method for printing the object.
ℹ️ There are many more and in fact, you can define custom protocols, Protocols are very generic and there is no official list of protocols although there are some pre-defined protocols in the typing module.
from typing import Iterator, Iterable, Optional, Sequence, Awaitable, Generic
🦆 Protocols empowers an approach called Duck Typing which is the fact that in Python if an object looks like, behaves like, and has the behavior of a Duck, it is said to be a Duck, regardless if this is the case of a Dog that learned to say quack immitating a Duck 🐶.
callable(print) is True
callable("Hello") is False
isinstance("Hello", str) is True
isinstance(0, slice) is False
hasattr("Hello", "__add__") is True # Summable, we can use `+` operator.
try:
"Hello" + 1
except TypeError: # Strong type checking
# we cannot `__add__` an `str` to an `int`
from typing import Protocol, runtime_checkable
@runtime_checkable
class CallableSummableSubscriptable(Protocol):
def __call__(self) -> T:
...
def __add__(self, other: T) -> T:
...
def __getitem__(self, item: T) -> T:
...
ℹ️ Protocol methods are just signatures with empty bodies, stated by the ...
. T
is usually a type alias indicating a generic type.
def awesome_function(thing: CallableSummableSubscriptable):
# accepts only objects that implements that ⬆️ protocol.
# Protocols can be checked at runtime @runtime_checkable
# Or checked using static analysers e.g: mypy
class MyString(str)
def __str__(self):
return super().__str__().upper()
>>> x = MyString("Bruno")
>>> print(x)
"BRUNO"
# Descriptor is a protocol for getter/setter like approach.
class Field:
def __get__(...):
def __set__(...):
class Thing:
# Double underline means that the field is private
# but actually it is only a naming mangling convention.
__protected_attr = Field()
# Properties can also be used to define getter/setter
@property
def foo(self):
return self.__protected_attr
@foo.setter
def set_foo(self, value):
self.__protected_attr = value
len("Bruno")
len([1, 2, 3])
dict.get("key")
dict.get("key", default="other")
print("Hello")
print(123)
print(*["Hello", "World"])
def function(*args, **kwargs):
...
ℹ️ In traditional OOP literature polymorphism is often used to define only the ability to have methods reusing the same name but different implementation, but in fact it goes deeper than this.
NOTE: Credits to @mathsppblog to have inspired the first paragraph of this post https://twitter.com/mathsppblog/status/1445148609977126914