Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(pydantic#Define fields to exclude from exporting at config level…
…): Define fields to exclude from exporting at config level Eagerly waiting for the release of the version 1.9 because you can [define the fields to exclude in the `Config` of the model](pydantic/pydantic#660) using something like: ```python class User(BaseModel): id: int username: str password: str class Transaction(BaseModel): id: str user: User value: int class Config: fields = { 'value': { 'alias': 'Amount', 'exclude': ..., }, 'user': { 'exclude': {'username', 'password'} }, 'id': { 'dump_alias': 'external_id' } } ``` The release it's taking its time because [the developer's gremlin and salaried work are sucking his time off](pydantic/pydantic#3228). feat(type_hints#Define a TypeVar with restrictions): Define a TypeVar with restrictions ```python from typing import TypeVar AnyStr = TypeVar('AnyStr', str, bytes) ``` feat(type_hints#Use a constrained TypeVar in the definition of a class attributes): Use a constrained TypeVar in the definition of a class attributes. If you try to use a `TypeVar` in the definition of a class attribute: ```python class File: """Model a computer file.""" path: str content: Optional[AnyStr] = None # mypy error! ``` [mypy](mypy.md) will complain with `Type variable AnyStr is unbound [valid-type]`, to solve it, you need to make the class inherit from the `Generic[AnyStr]`. ```python class File(Generic[AnyStr]): """Model a computer file.""" path: str content: Optional[AnyStr] = None ``` feat(python_properties): Give an overview on Python's @Property decorator fix(vim): Correct vim snippet to remember the folds when saving a file
- Loading branch information
Showing
5 changed files
with
294 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
--- | ||
title: Python Properties | ||
date: 20211118 | ||
author: Lyz | ||
--- | ||
|
||
The `@property` is the pythonic way to use getters and setters in | ||
object-oriented programming. It can be used to make methods look like attributes. | ||
|
||
The `property` decorator returns an object that proxies any request to set or | ||
access the attribute value through the methods we have specified. | ||
|
||
```python | ||
class Foo: | ||
@property | ||
def foo(self): | ||
return 'bar' | ||
``` | ||
|
||
We can specify a setter function for the new property | ||
|
||
```python | ||
class Foo: | ||
@property | ||
def foo(self): | ||
return self._foo | ||
|
||
@foo.setter | ||
def foo(self, value): | ||
self._foo = value | ||
``` | ||
|
||
We first decorate the `foo` method a as getter. Then we decorate a second method | ||
with exactly the same name by applying the `setter` attribute of the originally | ||
decorated `foo` method. The `property` function returns an object; this object | ||
always comes with its own `setter` attribute, which can then be applied as | ||
a decorator to other functions. Using the same name for the get and set methods | ||
is not required, but it does help group the multiple methods that access one | ||
property together. | ||
|
||
We can also specify a deletion function with `@foo.deleter`. We cannot specify | ||
a docstring using `property` decorators, so we need to rely on the property | ||
copying the docstring from the initial getter method | ||
|
||
```python | ||
class Silly: | ||
@property | ||
def silly(self): | ||
"This is a silly property" | ||
print("You are getting silly") | ||
return self._silly | ||
|
||
@silly.setter | ||
def silly(self, value): | ||
print("You are making silly {}".format(value)) | ||
self._silly = value | ||
|
||
@silly.deleter | ||
def silly(self): | ||
print("Whoah, you kicked silly!") | ||
del self.silly | ||
``` | ||
|
||
```python | ||
>>> s = Silly() | ||
>>> s.silly = "funny" | ||
You are making silly funny | ||
>>> s.silly | ||
You are getting silly | ||
'funny' | ||
>>> del s.silly | ||
Whoah, you kicked silly! | ||
``` | ||
|
||
# When to use properties | ||
|
||
The most common use of a property is when we have some data on a class that we | ||
later want to add behavior to. | ||
|
||
The fact that methods are just callable attributes, and properties are just | ||
customizable attributes can help us make the decision. Methods should typically | ||
represent actions; things that can be done to, or performed by, the object. When | ||
you call a method, even with only one argument, it should *do* something. Method | ||
names a generally verbs. | ||
|
||
Once confirming that an attribute is not an action, we need to decide between | ||
standard data attributes and properties. In general, always use a standard | ||
attribute until you need to control access to that property in some way. In | ||
either case, your attribute is usually a noun . The only difference between an | ||
attribute and a property is that we can invoke custom actions automatically when | ||
a property is retrieved, set, or deleted | ||
|
||
## Cache expensive data | ||
|
||
A common need for custom behavior is caching a value that is difficult to | ||
calculate or expensive to look up. | ||
|
||
We can do this with a custom getter on the property. The first time the value is | ||
retrieved, we perform the lookup or calculation. Then we could locally cache the | ||
value as a private attribute on our object, and the next time the value is | ||
requested, we return the stored data. | ||
|
||
```python | ||
from urlib.request import urlopen | ||
|
||
class Webpage: | ||
def __init__(self, url): | ||
self.url = url | ||
self._content = None | ||
|
||
@property | ||
def content(self): | ||
if not self._content: | ||
print("Retrieving New Page..") | ||
self._content = urlopen(self.url).read() | ||
return self._content | ||
``` | ||
|
||
```python | ||
>>> import time | ||
>>> webpage = Webpage("http://ccphillips.net/") | ||
>>> now = time.time() | ||
>>> content1 = webpage.content | ||
Retrieving new Page... | ||
>>> time.time() - now | ||
22.43316 | ||
>>> now = time.time() | ||
>>> content2 = webpage.content | ||
>>> time.time() -now | ||
1.926645 | ||
>>> content1 == content2 | ||
True | ||
``` | ||
|
||
## Attributes calculated on the fly | ||
|
||
Custom getters are also useful for attributes that need to be calculated on the | ||
fly, based on other object attributes. | ||
|
||
```python | ||
clsas AverageList(list): | ||
@property | ||
def average(self): | ||
return sum(self) / len(self) | ||
``` | ||
```python | ||
>>> a = AverageList([1,2,3,4]) | ||
>>> a.average | ||
2.5 | ||
``` | ||
|
||
Of course we could have made this a method instead, but then we should call it | ||
`calculate_average()`, since methods represent actions. But a property called | ||
`average` is more suitable, both easier to type, and easier to read. | ||
|
||
# [Abstract properties](https://stackoverflow.com/questions/5960337/how-to-create-abstract-properties-in-python-abstract-classes) | ||
|
||
Sometimes you want to define properties in your abstract classes, to do that, use: | ||
|
||
```python | ||
from abc import ABC, abstractmethod | ||
|
||
class C(ABC): | ||
@property | ||
@abstractmethod | ||
def my_abstract_property(self): | ||
... | ||
``` | ||
|
||
If you want to use an abstract setter, you'll encounter the mypy `Decorated | ||
property not supported` error, you'll need to add a `# type: ignore` until [this | ||
issue is solved](https://github.com/python/mypy/issues/1362). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters