python3.7用法_Python 3.7中dataclass装饰器用法详解
Python 3.7的dataclass裝飾器用法
Python 3.7新功能之dataclass裝飾器詳解
前言
Python 3.7 將于今年夏天發布,Python 3.7 中將會有許多新東西:
各種字符集的改進
對注釋的推遲評估
以及對dataclass的支持
最激動人心的新功能之一是 dataclass 裝飾器。
什么是 Data Class
大多數 Python 開發人員編寫過很多像下面這樣的類:
class MyClass:
def __init__(self, var_a, var_b):
self.var_a = var_a
self.var_b = var_b
dataclass 可以為簡單的情況自動生成方法,例如,一個__init__接受這些參數并將其分配給自己,之前的小例子可以重寫為:
@dataclass
class MyClass:
var_a: str
var_b: str
那么通過一個例子來看看如何使用吧
星球大戰 API
可以使用 requests 從星球大戰 API 獲取資源:
response = requests.get('https://swapi.co/api/films/1/')
dictionary = response.json()
讓我們來看看 dictionary (簡化過)的結果:
{
'characters': ['https://swapi.co/api/people/1/',… ],
'created': '2014-12-10T14:23:31.880000Z',
'director': 'George Lucas',
'edited': '2015-04-11T09:46:52.774897Z',
'episode_id': 4,
'opening_crawl': 'It is a period of civil war.\r\n … ',
'planets': ['https://swapi.co/api/planets/2/', … ],
'producer': 'Gary Kurtz, Rick McCallum',
'release_date': '1977-05-25',
'species': ['https://swapi.co/api/species/5/',…],
'starships': ['https://swapi.co/api/starships/2/',…],
'title': 'A New Hope',
'url': 'https://swapi.co/api/films/1/',
'vehicles': ['https://swapi.co/api/vehicles/4/',…]
封裝 API
為了正確地封裝一個 API,我們應該創建一個用戶可以在其應用程序中使用的對象,因此,在Python 3.6 中定義一個對象來包含requests對 /films/endpoint的響應:
class StarWarsMovie:
def __init__(self,
title: str,
episode_id: int,
opening_crawl: str,
director: str,
producer: str,
release_date: datetime,
characters: List[str],
planets: List[str],
starships: List[str],
vehicles: List[str],
species: List[str],
created: datetime,
edited: datetime,
url: str
):
self.title = title
self.episode_id = episode_id
self.opening_crawl= opening_crawl
self.director = director
self.producer = producer
self.release_date = release_date
self.characters = characters
self.planets = planets
self.starships = starships
self.vehicles = vehicles
self.species = species
self.created = created
self.edited = edited
self.url = url
if type(self.release_date) is str:
self.release_date = dateutil.parser.parse(self.release_date)
if type(self.created) is str:
self.created = dateutil.parser.parse(self.created)
if type(self.edited) is str:
self.edited = dateutil.parser.parse(self.edited)
仔細的讀者可能已經注意到這里有一些重復的代碼。
這是使用 dataclass 裝飾器的經典案例,我們需要創建一個主要用來保存數據的類,只需一點驗證,所以讓我們來看看我們需要修改什么。
首先,data class 自動生成一些 dunder 方法,如果我們沒有為 data class 裝飾器指定任何選項,則生成的方法有:__init__,__eq__和__repr__,如果你已經定義了__repr__但沒定義__str__,默認情況下 Python(不僅僅是 data class)將實現返回__repr__的輸出__str__方法。因此,只需將代碼更改為以下代碼即可實現四種 dunder 方法:
@dataclass
class StarWarsMovie:
title: str
episode_id: int
opening_crawl: str
director: str
producer: str
release_date: datetime
characters: List[str]
planets: List[str]
starships: List[str]
vehicles: List[str]
species: List[str]
created: datetime
edited: datetime
url: str
我們去掉了__init__方法,以確保 data class 裝飾器可以添加它生成的對應方法。不過,我們在這個過程中失去了一些功能,我們的 Python 3.6 構造函數不僅定義了所有的值,還試圖解析日期,我們怎樣才能用 data class 來做到這一點呢?
如果要覆蓋 __init__,我們將失去 data class 的優勢,因此,如果要處理任何附加功能可以使用新的 dunder 方法:__post_init__,讓我們看看__post_init__方法對于我們的包裝類來說是什么樣子的:
def __post_init__(self):
if type(self.release_date) is str:
self.release_date = dateutil.parser.parse(self.release_date)
if type(self.created) is str:
self.created = dateutil.parser.parse(self.created)
if type(self.edited) is str:
self.edited = dateutil.parser.parse(self.edited)
就是這樣! 我們可以使用 data class 裝飾器在用三分之二的代碼量實現我們的類。
更多好東西
通過使用裝飾器的選項,可以為用例進一步定制 data class,默認選項是:
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
init決定是否生成__init__ dunder 方法
repr決定是否生成__repr__ dunder方法
eq對__eq__ dunder 方法也是如此,它決定相等性檢查的行為(your_class_instance == another_instance)
order 實際上創建了四種 dunder 方法,它們確定所有檢查小于,and/or,大于的行為,如果將其設置為 true,則可以對對象列表進行排序。
最后兩個選項確定對象是否可以被哈希化,如果你想使用你的 class 的對象作為字典鍵的話,這是必要的。
python類裝飾器用法實例
本文實例講述了python類裝飾器用法。分享給大家供大家參考。具體如下:
#!coding=utf-8
registry = {}
def register(cls):
registry[cls.__clsid__] = cls
return cls
@register
class Foo(object):
__clsid__ = '123-456'
def bar(self):
pass
print registry
運行結果如下:
{'123-456': }
希望本文所述對大家的Python程序設計有所幫助。
Python 使用類寫裝飾器的小技巧
最近學到了一個有趣的裝飾器寫法,就記錄一下。
裝飾器是一個返回函數的函數。寫一個裝飾器,除了最常見的在函數中定義函數以外,Python還允許使用類來定義一個裝飾器。
1、用類寫裝飾器
下面用常見的寫法實現了一個緩存裝飾器。
def cache(func):
data = {}
def wrapper(*args, **kwargs):
key = f'{func.__name__}-{str(args)}-{str(kwargs)})'
if key in data:
result = data.get(key)
print('cached')
else:
result = func(*args, **kwargs)
data[key] = result
print('calculated')
return result
return wrapper
看看緩存的效果。
@cache
def rectangle_area(length, width):
return length * width
rectangle_area(2, 3)
# calculated
# 6
rectangle_area(2, 3)
# cached
# 6
裝飾器的@cache是一個語法糖,相當于func = cache(func),如果這里的cache不是一個函數,而是一個類又會怎樣呢?定義一個類class Cache, 那么調用func = Cache(func)會得到一個對象,這時返回的func其實是Cache的對象。定義__call__方法可以將類的實例變成可調用對象,可以像調用函數一樣調用對象。然后在__call__方法里調用原本的func函數就能實現裝飾器了。所以Cache類也能當作裝飾器使用,并且能以@Cache的形式使用。
接下來把cache函數改寫為Cache類:
class Cache:
def __init__(self, func):
self.func = func
self.data = {}
def __call__(self, *args, **kwargs):
func = self.func
data = self.data
key = f'{func.__name__}-{str(args)}-{str(kwargs)})'
if key in data:
result = data.get(key)
print('cached')
else:
result = func(*args, **kwargs)
data[key] = result
print('calculated')
return result
再看看緩存結果,效果一樣。
@Cache
def rectangle_area(length, width):
return length * width
rectangle_area(2, 3)
# calculated
# 6
rectangle_area(2, 3)
# cached
# 6
2、裝飾類的方法
裝飾器不止能裝飾函數,也經常用來裝飾類的方法,但是我發現用類寫的裝飾器不能直接用在裝飾類的方法上。(有點繞…)
先看看函數寫的裝飾器如何裝飾類的方法。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@cache
def area(self):
return self.length * self.width
r = Rectangle(2, 3)
r.area()
# calculated
# 6
r.area()
# cached
# 6
但是如果直接換成Cache類會報錯,這個錯誤的原因是area被裝飾后變成了類的一個屬性,而不是方法。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@Cache
def area(self):
return self.length * self.width
r = Rectangle(2, 3)
r.area()
# TypeError: area() missing 1 required positional argument: 'self'
Rectangle.area
# <__main__.Cache object at 0x0000012D8E7A6D30>
r.area
# <__main__.Cache object at 0x0000012D8E7A6D30>
回頭再來看看沒有裝飾器的情況,Python在實例化對象后把函數變成了方法。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def area(self):
return self.length * self.width
Rectangle.area
#
r = Rectangle(2, 3)
r.area
#
因此解決辦法很簡單,要用類寫的裝飾器來裝飾類的方法,只需要把可調用對象包裝成函數就行。
# 定義一個簡單的裝飾器,什么也不做,僅僅是把可調用對象包裝成函數
def method(call):
def wrapper(*args, **kwargs):
return call(*args, **kwargs)
return wrapper
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@method
@Cache
def area(self):
return self.length * self.width
r = Rectangle(2, 3)
r.area()
# calculated
# 6
r.area()
# cached
# 6
或者用@property還能直接把方法變成屬性。
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@property
@Cache
def area(self):
return self.length * self.width
r = Rectangle(2, 3)
r.area
# calculated
# 6
r.area
# cached
# 6
總結
用類寫裝飾器并非什么特別的技巧,一般情況下確實沒必要這么寫,不過這樣就可以用一些類的特性來寫裝飾器,比如類的繼承,也算是提供了另一種思路吧。
以上就是本次給大家分享的關于java的全部知識點內容總結,大家還可以在下方相關文章里找到相關文章進一步學習,感謝大家的閱讀和支持。
總結
以上是生活随笔為你收集整理的python3.7用法_Python 3.7中dataclass装饰器用法详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 预防食物中毒的安全知识
- 下一篇: java根据pdf模板生成pdf_Jav