Veri sınıfları nelerdir ve ortak sınıflardan nasıl farklıdır?


143

İle PEP 557 veri sınıfları piton standart kütüphanesine sokulur.

@dataclassDekoratörden faydalanıyorlar ve "değişken isimlendirilmiş çiftler" olmaları gerekiyor ama bunun gerçekte ne anlama geldiğini ve ortak sınıflardan ne kadar farklı olduklarını gerçekten anladığımdan emin değilim.

Python veri sınıfları tam olarak nedir ve bunları ne zaman kullanmak en iyisidir?


8
KEP'in kapsamlı içeriği göz önüne alındığında, başka ne bilmek isteyebilirsiniz? namedtuples değişmezdir ve öznitelikler için varsayılan değerlere sahip olamaz, oysa veri sınıfları değiştirilebilir ve bunlara sahip olabilir.
jonrsharpe

31
@jonrsharpe Konuyla ilgili bir stackoverflow dizisi olması bana mantıklı geliyor. Stackoverflow, Soru-Cevap formatında bir ansiklopedi olarak düşünülmüştür, değil mi? Cevap asla "sadece bu diğer web sitesine bakın." Burada olumsuz oylar olmamalıydı.
Luke Davis

12
Bir öğenin bir listeye nasıl ekleneceği ile ilgili beş konu vardır. Üzerine bir soru @dataclasssitenin dağılmasına neden olmaz.
eric

2
@jonrsharpe namedtuplesCAN varsayılan değerlere sahip olabilir. Buraya bir göz atın: stackoverflow.com/questions/11351032/…
MJB

Yanıtlar:


153

Veri sınıfları, çok fazla mantık içermekten çok, depolama durumuna yönelik düzenli sınıflardır. Çoğunlukla özniteliklerden oluşan bir sınıf oluşturduğunuzda, bir veri sınıfı oluşturdunuz.

Ne dataclassesmodül yapar bunu yapmak olduğunu daha kolay veri sınıfları oluşturmak için. Sizin için bir çok kazan tablası ile ilgilenir.

Bu, özellikle veri sınıfınızın hashable olması gerektiğinde önemlidir; bu bir __hash__yöntem olduğu kadar bir __eq__yöntemi de gerektirir . __repr__Hata ayıklama kolaylığı için özel bir yöntem eklerseniz , bu oldukça ayrıntılı hale gelebilir:

class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def __init__(
            self, 
            name: str, 
            unit_price: float,
            quantity_on_hand: int = 0
        ) -> None:
        self.name = name
        self.unit_price = unit_price
        self.quantity_on_hand = quantity_on_hand

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

    def __repr__(self) -> str:
        return (
            'InventoryItem('
            f'name={self.name!r}, unit_price={self.unit_price!r}, '
            f'quantity_on_hand={self.quantity_on_hand!r})'

    def __hash__(self) -> int:
        return hash((self.name, self.unit_price, self.quantity_on_hand))

    def __eq__(self, other) -> bool:
        if not isinstance(other, InventoryItem):
            return NotImplemented
        return (
            (self.name, self.unit_price, self.quantity_on_hand) == 
            (other.name, other.unit_price, other.quantity_on_hand))

İle dataclassesbunu şu şekilde azaltabilirsiniz:

from dataclasses import dataclass

@dataclass(unsafe_hash=True)
class InventoryItem:
    '''Class for keeping track of an item in inventory.'''
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

Aynı sınıf dekoratör de karşılaştırma yöntemleri (üretebilir __lt__, __gt__vs.) ve tutamak değişmezliğini.

namedtuplesınıflar da veri sınıflarıdır, ancak varsayılan olarak değişmezdir (aynı zamanda diziler olarak). dataclassesbu açıdan çok daha esnektir ve bir namedtuplesınıfla aynı rolü üstlenebilecekleri şekilde kolayca yapılandırılabilir .

PEP, daha da fazlasını yapabilen attrsprojeden ilham almıştır (slotlar, doğrulayıcılar, dönüştürücüler, meta veriler vb. Dahil).

Bazı örnekler görmek istiyorsanız, ben son kullanılan dataclassesbenim birkaç için Kod Advent için çözümler bkz çözümleri günden 7 , günde 8 , günde 11 ve günde 20 .

dataclassesModülü 3.7'nin altındaki Python sürümlerinde kullanmak istiyorsanız , o zaman arkaplanlı modülü kurabilir (3.6 gerektirir) veya attrsyukarıda belirtilen projeyi kullanabilirsiniz .


2
İlk örnekte, kasıtlı olarak aynı ada sahip örnek üyeleriyle sınıf üyelerini mi gizliyorsunuz? Lütfen bu deyimi anlamaya yardımcı olun.
VladimirLenin

4
@VladimirLenin: sınıf özniteliği yoktur, sadece tür açıklamaları vardır. Bkz PEP 526 , özellikle sınıf ve örnek değişkeni açıklamalar bölümü .
Martijn Pieters

1
@Bananach: varsayılan değeri olan bir anahtar kelime argümanıyla @dataclasskabaca aynı __init__yöntemi üretir quantity_on_hand. Bir örnek oluşturduğunuzda quantity_on_hand, her zaman örnek niteliğini ayarlar . Bu nedenle , veri sınıfı olmayan ilk örneğim, veri sınıfı tarafından oluşturulan kodun ne yapacağını yansıtmak için aynı modeli kullanıyor.
Martijn Pieters

1
@Bananach: böylece ilk örnekte, biz olabilir sadece omit bir örnek niteliğinin kurulması ve sınıf niteliğini gölge değil, o anlamda zaten gereksiz ayar öyle, ama dataclasses yok ayarlayın.
Martijn Pieters

1
@ user2853437 kullanım durumunuz gerçekten veri sınıfları tarafından desteklenmiyor; Belki de veri sınıflarının daha büyük kuzenini, çekimleri kullanmanız daha iyi olur . Bu proje, alan değerlerini normalleştirmenize izin veren alan başına dönüştürücüleri destekler . Veri sınıflarına bağlı kalmak istiyorsanız, o zaman evet, __post_init__yöntemde normalleştirme yapın .
Martijn Pieters

64

genel bakış

Soru ele alındı. Bununla birlikte, bu cevap, veri sınıflarının temel anlayışına yardımcı olacak bazı pratik örnekler ekler.

Python veri sınıfları tam olarak nedir ve bunları ne zaman kullanmak en iyisidir?

  1. kod üreteçleri : standart kod üretir; normal bir sınıfta özel yöntemler uygulamayı seçebilir veya bir veri sınıfının bunları otomatik olarak uygulamasını sağlayabilirsiniz.
  2. veri taşıyıcıları: genellikle sınıflar namedtupleve diğerleri gibi noktalı öznitelik erişimine sahip verileri tutan yapılar (örn. tuple ve dicts) .

"varsayılan [ler] ile değişken adlandırılmış çiftler"

İkinci ifade şu anlama gelir:

  • değiştirilebilir : varsayılan olarak, veri sınıfı nitelikleri yeniden atanabilir. İsteğe bağlı olarak onları değişmez hale getirebilirsiniz (aşağıdaki Örneklere bakın).
  • namedtuple : a namedtupleveya normal bir sınıf gibi noktalı, öznitelik erişiminiz var .
  • varsayılan : niteliklere varsayılan değerler atayabilirsiniz.

Yaygın sınıflarla karşılaştırıldığında, öncelikle standart kod yazmaktan tasarruf edersiniz.


Özellikleri

Bu, veri sınıfı özelliklerine genel bir bakıştır (TL; DR? Bir sonraki bölümdeki Özet Tablosuna bakın).

Ne olsun

Veri sınıflarından varsayılan olarak aldığınız özellikler şunlardır.

Nitelikler + Temsil + Karşılaştırma

import dataclasses


@dataclasses.dataclass
#@dataclasses.dataclass()                                       # alternative
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

Bu varsayılanlar, aşağıdaki anahtar kelimelerin otomatik olarak ayarlanmasıyla sağlanır True:

@dataclasses.dataclass(init=True, repr=True, eq=True)

Ne açabilirsin

Uygun anahtar sözcükler olarak ayarlanmışsa ek özellikler kullanılabilir True.

Sipariş

@dataclasses.dataclass(order=True)
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

Sıralama yöntemleri artık daha güçlü eşitlik testlerine < > <= >=benzer şekilde uygulanmaktadır (aşırı yükleme operatörleri functools.total_ordering:).

Hashable, Değişebilir

@dataclasses.dataclass(unsafe_hash=True)                        # override base `__hash__`
class Color:
    ...

Nesne potansiyel olarak değişebilir (muhtemelen istenmeyen) olsa da, bir hash uygulanır.

Hashable, Immutable

@dataclasses.dataclass(frozen=True)                             # `eq=True` (default) to be immutable 
class Color:
    ...

Artık bir karma uygulanmıştır ve nesneyi değiştirmeye veya özniteliklere atamaya izin verilmez.

Genel olarak, nesne ya unsafe_hash=Trueda ise hashable'dır frozen=True.

Daha fazla ayrıntı içeren orijinal karma mantık tablosuna da bakın.

Ne anlamıyorsun

Aşağıdaki özellikleri elde etmek için özel yöntemler manuel olarak uygulanmalıdır:

Ambalajın açılması

@dataclasses.dataclass
class Color:
    r : int = 0
    g : int = 0
    b : int = 0

    def __iter__(self):
        yield from dataclasses.astuple(self)

Optimizasyon

@dataclasses.dataclass
class SlottedColor:
    __slots__ = ["r", "b", "g"]
    r : int
    g : int
    b : int

Nesne boyutu artık küçültülmüştür:

>>> imp sys
>>> sys.getsizeof(Color)
1056
>>> sys.getsizeof(SlottedColor)
888

Bazı durumlarda, __slots__örnek oluşturma ve özniteliklere erişme hızını da artırır. Ayrıca, yuvalar varsayılan atamalara izin vermez; aksi takdirde, a ValueErroryükseltilir.

Bu blog gönderisindeki slotlar hakkında daha fazlasını görün .


Özet Tablosu

+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
|       Feature        |       Keyword        |                      Example                       |           Implement in a Class          |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+
| Attributes           |  init                |  Color().r -> 0                                    |  __init__                               |
| Representation       |  repr                |  Color() -> Color(r=0, g=0, b=0)                   |  __repr__                               |
| Comparision*         |  eq                  |  Color() == Color(0, 0, 0) -> True                 |  __eq__                                 |
|                      |                      |                                                    |                                         |
| Order                |  order               |  sorted([Color(0, 50, 0), Color()]) -> ...         |  __lt__, __le__, __gt__, __ge__         |
| Hashable             |  unsafe_hash/frozen  |  {Color(), {Color()}} -> {Color(r=0, g=0, b=0)}    |  __hash__                               |
| Immutable            |  frozen + eq         |  Color().r = 10 -> TypeError                       |  __setattr__, __delattr__               |
|                      |                      |                                                    |                                         |
| Unpacking+           |  -                   |  r, g, b = Color()                                 |   __iter__                              |
| Optimization+        |  -                   |  sys.getsizeof(SlottedColor) -> 888                |  __slots__                              |
+----------------------+----------------------+----------------------------------------------------+-----------------------------------------+

+ Bu yöntemler otomatik olarak oluşturulmaz ve bir veri sınıfında manuel uygulama gerektirir.

* __ne__ gerekli değildir ve bu nedenle uygulanmaz .


Ek özellikler

Sonrası başlatma

@dataclasses.dataclass
class RGBA:
    r : int = 0
    g : int = 0
    b : int = 0
    a : float = 1.0

    def __post_init__(self):
        self.a : int =  int(self.a * 255)


RGBA(127, 0, 255, 0.5)
# RGBA(r=127, g=0, b=255, a=127)

miras

@dataclasses.dataclass
class RGBA(Color):
    a : int = 0

Dönüşümler

Bir demet veya dict, bir dataclass dönüştürme yinelemeli :

>>> dataclasses.astuple(Color(128, 0, 255))
(128, 0, 255)
>>> dataclasses.asdict(Color(128, 0, 255))
{r: 128, g: 0, b: 255}

Sınırlamalar


Referanslar

  • R. Hettinger en tartışma üzerine Dataclasses: tüm kod jeneratörleri sona erdirmek için kod üreteci
  • T. Hunner konuşma üzerine cruft All olmadan Python Sınıflar: Daha kolay Sınıflar
  • Karma ayrıntılarıyla ilgili Python'un belgeleri
  • Gerçek Python'un kılavuzu üzerinde Python 3.7 Veri Sınıflara Ultimate Guide
  • A. Shaw'un blog yazısı üzerine Python 3.7 veri sınıflarının kısa turu
  • E. Smith'in veri sınıfları üzerindeki github deposu

2

Kaynaktan PEP tarifnamede :

PEP 526, "Değişken Ek Açıklamaları için Sözdizimi" içinde tanımlandığı gibi değişkenler için bir sınıf tanımını inceleyen bir sınıf dekoratörü sağlanır. Bu belgede, bu tür değişkenler alanlar olarak adlandırılır. Bu alanları kullanarak, dekoratör, örnek başlatma, repr, karşılaştırma yöntemleri ve isteğe bağlı olarak Spesifikasyon bölümünde açıklanan diğer yöntemleri desteklemek için oluşturulan yöntem tanımlarını sınıfa ekler. Böyle bir sınıfa Veri Sınıfı denir, ancak sınıfla ilgili özel bir şey yoktur: dekoratör, üretilen yöntemleri sınıfa ekler ve verildiği sınıfı döndürür.

@dataclassJeneratör aksi gibi kendini tanımlamak edeceğimi sınıfa yöntemlerini ekler __repr__, __init__, __lt__, ve __gt__.


2

Bu basit sınıfı düşünün Foo

from dataclasses import dataclass
@dataclass
class Foo:    
    def bar():
        pass  

İşte dir()yerleşik karşılaştırma. Sol tarafta Foo@dataclass dekoratörü olmadan ve sağ tarafta @dataclass dekoratörüyle yer alır.

görüntü açıklamasını buraya girin

inspectKarşılaştırma için modülü kullandıktan sonra başka bir fark var .

görüntü açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.