Python'daki C benzeri yapılar


447

Python'da C benzeri bir yapıyı uygun şekilde tanımlamanın bir yolu var mı? Gibi şeyler yazmaktan bıktım:

class MyStruct():
    def __init__(self, field1, field2, field3):
        self.field1 = field1
        self.field2 = field2
        self.field3 = field3

5
Yarı ilgili olarak, cebirsel veri türleri kesinlikle harika olurdu, ancak bunları iyi kullanmak için genellikle desen eşleştirmeye ihtiyacınız vardır.
Edward Z. Yang

51
Bu yöntemle yazmak sıkıcı olmaktan başka yanlış bir şey var mı?
levesque

2
Dstruct'i yararlı bulabilirsiniz: github.com/dorkitude/dstruct
Kyle Wild

10
@ levesque yazım hataları olmadan yeniden çarpanlarına ayırmak zor, kod gözden geçirme sırasında bir bakışta okumak daha zor, dahaMyStruct = namedtuple("MyStruct", "field1 field2 field3")
sam boosalis

1
pandas.Series(a=42).aeğer bir veri bilim
Mark Horvath

Yanıtlar:


341

Python 2.6'daki standart kitaplıktaki koleksiyon modülüne eklenen adlandırılmış bir grup kullanın . Python 2.4'ü desteklemeniz gerekiyorsa Raymond Hettinger'in adlandırılmış tuple reçetesini kullanmak da mümkündür .

Temel örneğiniz için güzel, ancak daha sonra da karşılaşabileceğiniz bir dizi kenar durumu da kapsar. Yukarıdaki parçanız şu şekilde yazılır:

from collections import namedtuple
MyStruct = namedtuple("MyStruct", "field1 field2 field3")

Yeni oluşturulan tür şu şekilde kullanılabilir:

m = MyStruct("foo", "bar", "baz")

Adlandırılmış bağımsız değişkenleri de kullanabilirsiniz:

m = MyStruct(field1="foo", field2="bar", field3="baz")

164
... ama adlandırılmış grup değişmez. OP'deki örnek değiştirilebilir.
mhowison

28
@mhowison - Benim durumumda, bu sadece bir artı.
ArtOfWarfare

3
Güzel çözüm. Bu tuples dizisini nasıl dolaşırsınız? 1-3 alanlarının demet nesneleri arasında aynı isimlere sahip olması gerektiğini varsayabilirim.
Michael Smith

2
namedtuple en az dört argümana sahip olabilir, bu nedenle ilgili adlandırılmıştuple ile daha fazla veri üyesi ile yapıyı nasıl haritalayabiliriz
Kapil

3
@Kapil - namedtuple ile ilgili ikinci argüman üye adlarının bir listesi olmalıdır. Bu liste herhangi bir uzunlukta olabilir.
ArtOfWarfare

228

Güncelleme : Veri Sınıfları

Tanıtılmasıyla Veri Sınıflar içinde Python 3.7 çok yakın olsun.

Aşağıdaki örnek, aşağıdaki NamedTuple örneğine benzer , ancak ortaya çıkan nesne değiştirilebilir ve varsayılan değerlere izin verir.

from dataclasses import dataclass


@dataclass
class Point:
    x: float
    y: float
    z: float = 0.0


p = Point(1.5, 2.5)

print(p)  # Point(x=1.5, y=2.5, z=0.0)

Daha spesifik tür ek açıklamaları kullanmak istemeniz durumunda , bu yeni yazma modülüyle güzelce oynar .

Bunun için umutsuzca bekliyordum! Bana sorarsan, Veri Sınıfları ve yeni NamedTuple bildirimi, yazım modülü ile birlikte bir nimettir!

Geliştirilmiş NamedTuple bildirimi

Dan beri Python 3.6'dan değişmezlikle yaşayabildiğiniz sürece oldukça basit ve güzel (IMHO) oldu .

Bir NamedTuples ilan yeni bir yol için izin veren tanıtıldı tip ek açıklamalar yanı:

from typing import NamedTuple


class User(NamedTuple):
    name: str


class MyStruct(NamedTuple):
    foo: str
    bar: int
    baz: list
    qux: User


my_item = MyStruct('foo', 0, ['baz'], User('peter'))

print(my_item) # MyStruct(foo='foo', bar=0, baz=['baz'], qux=User(name='peter'))

6
Dostum, az önce günümü yaptın - değişmez dikte - teşekkür ederim: D
Dmitry Arkhipenko

10
dataclassModül Python 3.7 yeni ama can pip install dataclasses. Bu Python 3.6'daki backport. pypi.org/project/dataclasses/#description
Lavande

Geliştirilmiş NamedTuple bildirimi için +1. Birkaç değişkeniniz varsa eski yol okumak gerçekten tatsızdı ...
gebbissimo

@ Levande 3.6 ve 3.7 arasında bir küçük sürümü geri almak zorunda kaldığınız değişiklikleri nasıl bilebilirim?
Mor Buz

1
@PurpleIce PEP 557, Veri Sınıflarının bir uygulamasıydı @dataclassAyrıntılar burada: pypi.org/project/dataclasses/#description
Lavande

96

C'de bir yapı kullanacağınız birçok şey için bir demet kullanabilirsiniz (örneğin x, y koordinatları veya RGB renkleri gibi).

Diğer her şey için size sözlük, ya da benzeri bir yardımcı sınıf kullanabilirsiniz bu bir :

>>> class Bunch:
...     def __init__(self, **kwds):
...         self.__dict__.update(kwds)
...
>>> mystruct = Bunch(field1=value1, field2=value2)

Ben "kesin" tartışması olduğunu düşünüyorum burada Python Cookbook yayınlanan sürümde.


5
Boş bir sınıf da aynısını yapar mı?
Kurt Liu


2
@KurtLiu Hayır, muhtemelen söyleyebilirimTypeError: this constructor takes no arguments
Evgeni Sergeev

84

Belki de kurucu olmayan Yapılar arıyorsunuz:

class Sample:
  name = ''
  average = 0.0
  values = None # list cannot be initialized here!


s1 = Sample()
s1.name = "sample 1"
s1.values = []
s1.values.append(1)
s1.values.append(2)
s1.values.append(3)

s2 = Sample()
s2.name = "sample 2"
s2.values = []
s2.values.append(4)

for v in s1.values:   # prints 1,2,3 --> OK.
  print v
print "***"
for v in s2.values:   # prints 4 --> OK.
  print v

5
Burada yaptıklarınız teknik olarak çalışır, ancak muhtemelen birçok kullanıcının neden çalıştığını hemen göstermez. Altındaki beyanlarınız class Sample:hemen hiçbir şey yapmaz; sınıf öznitelikleri ayarlarlar. Bunlara her zaman örn Sample.name.
Channing Moore

22
Ne konum aslında yapıyor nesnelere Örnek özelliklerini ekleyerek olduğunu s1ve s2zamanında. Aksi yasaklanmadığı sürece name, sınıfın bir nameniteliği olsa da olmasa da, herhangi bir sınıfın herhangi bir örneğinde niteliği istediğiniz zaman ekleyebilir veya değiştirebilirsiniz . Muhtemelen bunu yapmanın en büyük fonksiyonel sorunu, aynı sınıfın farklı örneklerinin ayarladığınıza bağlı olarak farklı davranmasıdır name. Güncelleme yaparsanız Sample.name, açıkça ayarlanmış bir nameözelliği olmayan nesneler yeniyi döndürür name.
Channing Moore

2
Bu, yöntemsiz bir yapı - kısa 'sınıf', varsayılan değerler ile 'alanlar' (sınıf öznitelikleri, biliyorum) alır. Değişken bir tip olmadığı sürece (dikte, liste), iyisin. Tabii ki, PyCharm'ın "sınıfının init yöntemi yok" gibi PEP-8 veya "dost" IDE kontrollerine karşı vurabilirsiniz .
Tomasz Gandor

4
Channing Moore tarafından tarif edilen yan etkiyi denedim. selfBana sorarsanız bir kaç anahtar kelime ve bir yapıcı hattı ekonomisi değmez . Jose'nin örnekleri yanlışlıkla yanlışlıkla paylaşma riski hakkında bir uyarı mesajı eklemek için cevabını düzenleyip düzenleyemeyeceğini takdir ediyorum.
Stéphane

@ChanningMoore: Açıkladığınız sorunu yeniden oluşturmaya çalıştım, ancak başarısız oldum. Sorunun ortaya çıktığı asgari bir çalışma örneği sunabilir misiniz?
gebbissimo

67

Bir sözlüğe ne dersiniz?

Bunun gibi bir şey:

myStruct = {'field1': 'some val', 'field2': 'some val'}

Ardından, değerleri değiştirmek için bunu kullanabilirsiniz:

print myStruct['field1']
myStruct['field2'] = 'some other values'

Ve değerlerin dize olması gerekmez. Hemen hemen başka herhangi bir nesne olabilirler.


34
Bu benim yaklaşımım da oldu, ama tam olarak tehlikeli olduğunu hissediyorum, çünkü bir sözlük bir anahtar için her şeyi kabul edebilir. MyStruct ["field"] 'ı ayarlamak istediğimde myStruct ["ffield"]' i ayarlarsam hata olmaz. Daha sonra myStruct ["field"] kullandığımda veya yeniden kullandığımda sorun belirginleşebilir (veya olmayabilir). PabloG'un yaklaşımını seviyorum.
mobabo

PabloG'lerde de aynı sorun var. Aşağıdaki kodu onun için eklemeyi deneyin: pt3.w = 1 print pt3.w Dikteleri olan bir dilde, özellikle serileştirilmiş nesneler için bunları kullanmak daha iyidir, çünkü garip olmadığınız sürece onları ve diğer serileştirme kitaplıklarını kaydetmek için import json'u kullanabilirsiniz. dikte içindeki şeyler. Dicts, verileri ve mantığı ayrı tutma çözümüdür ve özel serileştirme ve serileştirme işlevleri yazmak istemeyen ve turşu gibi taşınabilir olmayan serileştiricileri kullanmak istemeyen kişiler için yapılardan daha iyidir.
Poikilos

27

dF: bu çok güzel ... Bir sınıftaki alanlara dikte kullanarak erişebileceğimi bilmiyordum.

Mark: Keşke bunun olmasını istediğim durumlar tam olarak bir demet istediğimde ama sözlük olarak "ağır" bir şey olmadığında.

Bir sınıfın alanları, yöntemleri ve tüm özellikleri dicts kullanılarak dahili olarak depolandığından (en azından CPython'da) bir sınıfın alanlarına sözlük kullanarak erişebilirsiniz.

... bu da bizi ikinci yorumunuza götürüyor. Python diktatörlerinin "ağır" olduğuna inanmak son derece pythonistic bir kavramdır. Ve bu yorumları okumak Python Zen'imi öldürüyor. Bu iyi değil.

Gördüğünüz gibi, bir sınıf ilan ettiğinizde, aslında bir sözlük etrafında oldukça karmaşık bir paket oluşturuyorsunuz - yani, eğer bir şey varsa, basit bir sözlük kullanmaktan daha fazla yük eklersiniz. Bu arada, her durumda anlamsız olan bir ek yük. Performans açısından kritik uygulamalar üzerinde çalışıyorsanız, C veya başka bir şey kullanın.


5
# 1, Cython! = CPython. Bence Python kodunu C koduna derlemek için bir proje olan Cython değil, C ile yazılmış Python uygulaması CPython hakkında konuşuyordunuz. Bunu düzeltmek için cevabınızı düzenledim. # 2, sanırım zorluklar ağır olduğunda sözdiziminden bahsediyordu. self['member']3 karakterden daha uzun self.memberve bu karakterlerin hepsi bilekten bile rahat.
ArtOfWarfare

19

Standart kitaplıkta bulunan C yapısını alt sınıflandırabilirsiniz. Ctypes modülü sağlar Yapısı sınıfı . Dokümanlardan örnek:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = [("x", c_int),
...                 ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print point.x, point.y
10 20
>>> point = POINT(y=5)
>>> print point.x, point.y
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
ValueError: too many initializers
>>>
>>> class RECT(Structure):
...     _fields_ = [("upperleft", POINT),
...                 ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print rc.upperleft.x, rc.upperleft.y
0 5
>>> print rc.lowerright.x, rc.lowerright.y
0 0
>>>

18

Ayrıca yuvaları kullanan bir çözüm eklemek istiyorum :

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

Yuvalar için belgeleri kesinlikle kontrol edin, ancak yuvaların hızlı bir açıklaması, python'un şöyle demesidir: "Bu nitelikleri ve yalnızca bu nitelikleri sınıfa kilitleyebilirseniz, sınıftan sonra yeni nitelikler eklemeyeceğinizi taahhüt edersiniz somutlaştırılır (evet, bir sınıf örneğine yeni öznitelikler ekleyebilirsiniz, aşağıdaki örneğe bakın) o zaman bir sınıf örneğine yeni öznitelikler eklemeye ve bu oluklu için ihtiyacım olanı kullanmamıza izin veren büyük bellek ayırmayı ortadan kaldıracağım öznitelikler ".

Sınıf örneğine nitelik ekleme örneği (bu nedenle yuva kullanmamak):

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8
print(p1.z)

Çıktı: 8

Yuvaların kullanıldığı sınıf örneğine nitelikler eklemeye çalışma örneği:

class Point:
    __slots__ = ["x", "y"]
    def __init__(self, x, y):
        self.x = x
        self.y = y

p1 = Point(3,5)
p1.z = 8

Çıktı: AttributeError: 'Point' nesnesinin 'z' özelliği yok

Bu etkili bir yapı olarak çalışır ve bir sınıftan daha az bellek kullanır (tam olarak ne kadar araştırmamış olsam da, bir yapı gibi). Nesnenin çok sayıda örneğini oluşturuyorsanız ve nitelik eklemeniz gerekmiyorsa, yuvaların kullanılması önerilir. Bir nokta nesnesi buna iyi bir örnektir, çünkü bir veri kümesini tanımlamak için birçok noktayı başlatabilir.


17

İnit parametrelerini konuma göre örnek değişkenlerine iletebilirsiniz

# Abstract struct class       
class Struct:
    def __init__ (self, *argv, **argd):
        if len(argd):
            # Update by dictionary
            self.__dict__.update (argd)
        else:
            # Update by position
            attrs = filter (lambda x: x[0:2] != "__", dir(self))
            for n in range(len(argv)):
                setattr(self, attrs[n], argv[n])

# Specific class
class Point3dStruct (Struct):
    x = 0
    y = 0
    z = 0

pt1 = Point3dStruct()
pt1.x = 10

print pt1.x
print "-"*10

pt2 = Point3dStruct(5, 6)

print pt2.x, pt2.y
print "-"*10

pt3 = Point3dStruct (x=1, y=2, z=3)
print pt3.x, pt3.y, pt3.z
print "-"*10

7
Konuma göre güncelleme, özniteliklerin bildirim sırasını yoksayar ve bunun yerine alfabetik sıralama kullanır. Dolayısıyla, satır sırasını Point3dStructbildirimde değiştirirseniz, Point3dStruct(5, 6)beklendiği gibi çalışmaz. 6 yıl boyunca hiç kimsenin bunu yazmaması garip.
lapis

Harika kodunuza bir Python 3 sürümü ekleyebilir misiniz? Harika iş! Ben soyut bir şey almak ve ikinci sınıf ile açık yapmak hoşuma gitti. Hata işleme / yakalama için iyi olmalıdır. Python 3 için, print> print()ve attrs[n]> öğesini değiştirin next(attrs)(şimdi filtre kendi yinelenebilir nesnesidir ve gerektirir next).
Jonathan Komar

10

(Ben bir "aynı zamanda bir sözlük gibi davranır anlık veri nesnesi" her ihtiyacın yok ! C yapılar düşünün), bu sevimli kesmek düşünüyorum:

class Map(dict):
    def __init__(self, **kwargs):
        super(Map, self).__init__(**kwargs)
        self.__dict__ = self

Şimdi sadece şunu söyleyebilirsiniz:

struct = Map(field1='foo', field2='bar', field3=42)

self.assertEquals('bar', struct.field2)
self.assertEquals(42, struct['field3'])

"Sınıf DEĞİL bir veri çantasına" ihtiyaç duyduğunuz zamanlar ve adlandırılmış üçlülerin anlaşılmaz olduğu zamanlar için mükemmel ...


Pandaları kullanıyorum.Series (a = 42) ;-)
Mark Horvath

8

C-Style yapısına python ile aşağıdaki şekilde erişirsiniz.

class cstruct:
    var_i = 0
    var_f = 0.0
    var_str = ""

sadece cstruct nesnesini kullanmak istiyorsanız

obj = cstruct()
obj.var_i = 50
obj.var_f = 50.00
obj.var_str = "fifty"
print "cstruct: obj i=%d f=%f s=%s" %(obj.var_i, obj.var_f, obj.var_str)

cstruct nesnelerinden oluşan bir dizi oluşturmak istiyorsanız

obj_array = [cstruct() for i in range(10)]
obj_array[0].var_i = 10
obj_array[0].var_f = 10.00
obj_array[0].var_str = "ten"

#go ahead and fill rest of array instaces of struct

#print all the value
for i in range(10):
    print "cstruct: obj_array i=%d f=%f s=%s" %(obj_array[i].var_i, obj_array[i].var_f, obj_array[i].var_str)

Not: 'cstruct' adı yerine, lütfen var_i, var_f, var_str yerine yapı adınızı kullanın, lütfen yapınızın üye değişkenini tanımlayın.



8

Buradaki bazı cevaplar çok ayrıntılı. Bulduğum en basit seçenek ( http://norvig.com/python-iaq.html adresinden ):

class Struct:
    "A structure that can have any fields defined."
    def __init__(self, **entries): self.__dict__.update(entries)

başlatılması Açıklama:

>>> options = Struct(answer=42, linelen=80, font='courier')
>>> options.answer
42

daha fazla ekleme:

>>> options.cat = "dog"
>>> options.cat
dog

edit: Üzgünüm bu örneği daha da aşağı görmedim.


5

Bu biraz geç olabilir ama ben Python Meta-Sınıfları (aşağıda dekoratör sürümü de) kullanarak bir çözüm yaptı.

Ne zaman __init__ çalışma zamanı sırasında denir, bu argümanlar ve bunların değeri ve sınıfınıza örnek değişkenleri olarak atar onları her yakalar. Bu şekilde, her değeri manuel olarak atamak zorunda kalmadan yapı benzeri bir sınıf oluşturabilirsiniz.

Örneğimde hata denetimi yok, bu yüzden takip etmek daha kolay.

class MyStruct(type):
    def __call__(cls, *args, **kwargs):
        names = cls.__init__.func_code.co_varnames[1:]

        self = type.__call__(cls, *args, **kwargs)

        for name, value in zip(names, args):
            setattr(self , name, value)

        for name, value in kwargs.iteritems():
            setattr(self , name, value)
        return self 

İşte burada.

>>> class MyClass(object):
    __metaclass__ = MyStruct
    def __init__(self, a, b, c):
        pass


>>> my_instance = MyClass(1, 2, 3)
>>> my_instance.a
1
>>> 

Ben reddit ve / u / matchu yayınlanmıştır temiz olan bir dekoratör sürümü yayınladı. Metaclass versiyonunu genişletmek istemiyorsanız onu kullanmanızı tavsiye ederim.

>>> def init_all_args(fn):
    @wraps(fn)
    def wrapped_init(self, *args, **kwargs):
        names = fn.func_code.co_varnames[1:]

        for name, value in zip(names, args):
            setattr(self, name, value)

        for name, value in kwargs.iteritems():
            setattr(self, name, value)

    return wrapped_init

>>> class Test(object):
    @init_all_args
    def __init__(self, a, b):
        pass


>>> a = Test(1, 2)
>>> a.a
1
>>> 

Damnit - Bugün bunu yapmak için iki saatimi kendi dekoratörlerimi yazdım ve sonra buldum. Her neyse, benimki göndermiyor çünkü varsayılan değerleri işliyor, sizinkinde değil. stackoverflow.com/a/32448434/901641
ArtOfWarfare

Func_code'dan bahsetmek için +1. Bu yönde kazmaya başladı ve orada birçok ilginç şey buldu.
wombatonfire

5

Geçirilen tüm argümanların veya herhangi bir varsayılanın örneğe atanması için herhangi bir yöntemde kullanabileceğiniz bir dekoratör yazdım.

def argumentsToAttributes(method):
    argumentNames = method.func_code.co_varnames[1:]

    # Generate a dictionary of default values:
    defaultsDict = {}
    defaults = method.func_defaults if method.func_defaults else ()
    for i, default in enumerate(defaults, start = len(argumentNames) - len(defaults)):
        defaultsDict[argumentNames[i]] = default

    def newMethod(self, *args, **kwargs):
        # Use the positional arguments.
        for name, value in zip(argumentNames, args):
            setattr(self, name, value)

        # Add the key word arguments. If anything is missing, use the default.
        for name in argumentNames[len(args):]:
            setattr(self, name, kwargs.get(name, defaultsDict[name]))

        # Run whatever else the method needs to do.
        method(self, *args, **kwargs)

    return newMethod

Hızlı bir gösteri. Konum bağımsız değişkenini akullandığımı, için varsayılan değeri bve adlandırılmış bir bağımsız değişkeni kullandığımı unutmayın c. Daha sonra selfyöntem girilmeden önce doğru şekilde atandıklarını göstermek için 3 referansın tümünü yazdırıyorum .

class A(object):
    @argumentsToAttributes
    def __init__(self, a, b = 'Invisible', c = 'Hello'):
        print(self.a)
        print(self.b)
        print(self.c)

A('Why', c = 'Nothing')

Dekoratörümün sadece herhangi bir yöntemle çalışması gerektiğini unutmayın __init__.


5

Bu cevabı burada görmüyorum, bu yüzden şimdi Python'u eğdiğimden ve keşfettiğimden ekleyeceğim. Python öğretici (bu durumda Python 2) Aşağıdaki basit ve etkili örnek verir:

class Employee:
    pass

john = Employee()  # Create an empty employee record

# Fill the fields of the record
john.name = 'John Doe'
john.dept = 'computer lab'
john.salary = 1000

Yani, boş bir sınıf nesnesi oluşturulur, sonra başlatılır ve alanlar dinamik olarak eklenir.

Bunun üst tarafı gerçekten basit. Dezavantajı, özellikle kendi kendini belgelememesi (hedeflenen üyelerin "tanım" sınıfında herhangi bir yerde listelenmemesi) ve ayarlanmamış alanlar erişildiğinde sorunlara neden olabilir. Bu iki problem şu şekilde çözülebilir:

class Employee:
    def __init__ (self):
        self.name = None # or whatever
        self.dept = None
        self.salary = None

Şimdi bir bakışta en azından programın hangi alanları bekleyeceğini görebilirsiniz.

Her ikisi de yazım hatalarına eğilimlidir john.slarly = 1000, başarılı olur. Yine de çalışıyor.


4

Verileri tutmak için bir sınıfı (hiçbir zaman somutlaştırılmamış) kullanan bir çözüm. Bu şekilde çok az yazmayı içerir ve herhangi bir ek paket vb. Gerektirmez .

class myStruct:
    field1 = "one"
    field2 = "2"

Daha sonra gerektiği gibi daha fazla alan ekleyebilirsiniz:

myStruct.field3 = 3

Değerleri almak için alanlara her zamanki gibi erişilir:

>>> myStruct.field1
'one'

2

Şahsen ben de bu varyantı seviyorum. @ DF'nin cevabını uzatır .

class struct:
    def __init__(self, *sequential, **named):
        fields = dict(zip(sequential, [None]*len(sequential)), **named)
        self.__dict__.update(fields)
    def __repr__(self):
        return str(self.__dict__)

İki başlatma modunu destekler (karıştırılabilir):

# Struct with field1, field2, field3 that are initialized to None.
mystruct1 = struct("field1", "field2", "field3") 
# Struct with field1, field2, field3 that are initialized according to arguments.
mystruct2 = struct(field1=1, field2=2, field3=3)

Ayrıca, daha güzel yazdırır:

print(mystruct2)
# Prints: {'field3': 3, 'field1': 1, 'field2': 2}

2

Bir yapıya aşağıdaki çözüm, adlandırılmış uygulama ve önceki yanıtlardan bazılarından esinlenmiştir. Bununla birlikte, adlandırılmış diziden farklı olarak, değerlerinde değişebilir, ancak normal bir sınıf veya diktenin olmadığı adlarda / özniteliklerde değişmeyen c stili yapı gibi.

_class_template = """\
class {typename}:
def __init__(self, *args, **kwargs):
    fields = {field_names!r}

    for x in fields:
        setattr(self, x, None)            

    for name, value in zip(fields, args):
        setattr(self, name, value)

    for name, value in kwargs.items():
        setattr(self, name, value)            

def __repr__(self):
    return str(vars(self))

def __setattr__(self, name, value):
    if name not in {field_names!r}:
        raise KeyError("invalid name: %s" % name)
    object.__setattr__(self, name, value)            
"""

def struct(typename, field_names):

    class_definition = _class_template.format(
        typename = typename,
        field_names = field_names)

    namespace = dict(__name__='struct_%s' % typename)
    exec(class_definition, namespace)
    result = namespace[typename]
    result._source = class_definition

    return result

Kullanımı:

Person = struct('Person', ['firstname','lastname'])
generic = Person()
michael = Person('Michael')
jones = Person(lastname = 'Jones')


In [168]: michael.middlename = 'ben'
Traceback (most recent call last):

  File "<ipython-input-168-b31c393c0d67>", line 1, in <module>
michael.middlename = 'ben'

  File "<string>", line 19, in __setattr__

KeyError: 'invalid name: middlename'

2

Tam olarak bu amaç için bir python paketi var. bkz. cstruct2py

cstruct2pyC kodundan python sınıfları oluşturmak ve bunları paketlemek ve paketten çıkarmak için kullanmak için saf bir python kütüphanesidir. Kütüphane C başlıklarını ayrıştırabilir (yapılar, birlikler, numaralar ve diziler bildirimleri) ve bunları python'da taklit edebilir. Oluşturulan pitonik sınıflar verileri ayrıştırabilir ve paketleyebilir.

Örneğin:

typedef struct {
  int x;
  int y;
} Point;

after generating pythonic class...
p = Point(x=0x1234, y=0x5678)
p.packed == "\x34\x12\x00\x00\x78\x56\x00\x00"

Nasıl kullanılır

İlk önce pitonik yapıları üretmeliyiz:

import cstruct2py
parser = cstruct2py.c2py.Parser()
parser.parse_file('examples/example.h')

Şimdi tüm isimleri C kodundan içe aktarabiliriz:

parser.update_globals(globals())

Bunu doğrudan da yapabiliriz:

A = parser.parse_string('struct A { int x; int y;};')

C kodundan türleri ve tanımları kullanma

a = A()
a.x = 45
print a
buf = a.packed
b = A(buf)
print b
c = A('aaaa11112222', 2)
print c
print repr(c)

Çıktı şöyle olacaktır:

{'x':0x2d, 'y':0x0}
{'x':0x2d, 'y':0x0}
{'x':0x31316161, 'y':0x32323131}
A('aa111122', x=0x31316161, y=0x32323131)

Klon

Klonlama cstruct2pyçalışması için:

git clone https://github.com/st0ky/cstruct2py.git --recursive

0

Bence Python yapı sözlüğü bu gereklilik için uygun.

d = dict{}
d[field1] = field1
d[field2] = field2
d[field2] = field3

0

https://stackoverflow.com/a/32448434/159695 Python3'te çalışmaz.

https://stackoverflow.com/a/35993/159695 Python3'te çalışır.

Ve varsayılan değerleri eklemek için genişletir.

class myStruct:
    def __init__(self, **kwds):
        self.x=0
        self.__dict__.update(kwds) # Must be last to accept assigned member variable.
    def __repr__(self):
        args = ['%s=%s' % (k, repr(v)) for (k,v) in vars(self).items()]
        return '%s(%s)' % ( self.__class__.__qualname__, ', '.join(args) )

a=myStruct()
b=myStruct(x=3,y='test')
c=myStruct(x='str')

>>> a
myStruct(x=0)
>>> b
myStruct(x=3, y='test')
>>> c
myStruct(x='str')

0

@Dataclass için 3.7'niz yoksa ve değiştirilebilirliğe ihtiyacınız varsa, aşağıdaki kod sizin için işe yarayabilir. Oldukça kendi kendini belgeleyen ve IDE dostu (otomatik tamamlama), iki kez yazmayı önler, kolayca genişletilebilir ve tüm örnek değişkenlerin tamamen başlatıldığını test etmek çok basittir:

class Params():
    def __init__(self):
        self.var1 : int = None
        self.var2 : str = None

    def are_all_defined(self):
        for key, value in self.__dict__.items():
            assert (value is not None), "instance variable {} is still None".format(key)
        return True


params = Params()
params.var1 = 2
params.var2 = 'hello'
assert(params.are_all_defined)

0

İşte hızlı ve kirli bir numara:

>>> ms = Warning()
>>> ms.foo = 123
>>> ms.bar = 'akafrit'

Nasıl çalışır? Sadece builtin sınıfını Warning(türetilmiş Exception) yeniden kullanır ve kendi tanımlanmış sınıfınız gibi kullanır.

İyi nokta, ilk önce herhangi bir şey içe aktarmanız veya tanımlamanız gerekmediği, "Uyarı" nın kısa bir ad olduğu ve küçük bir komut dosyasından başka bir yerde kullanılmaması gereken kirli bir şey yaptığınızı da açıkça belirtmesidir.

Bu arada, daha da basit bir şey bulmaya çalıştım ms = object()ama yapamadım (bu son örnek çalışmıyor). Eğer bir tane varsa, ilgileniyorum.


0

Bunu yapmanın en iyi yolu, bu yayında açıklandığı gibi özel bir sözlük sınıfı kullanmaktı: https://stackoverflow.com/a/14620633/8484485

İPython otomatik tamamlama desteği gerekiyorsa, dir () işlevini şu şekilde tanımlamanız yeterlidir :

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self
    def __dir__(self):
        return self.keys()

Daha sonra sözde yapınızı şöyle tanımlarsınız: (bu yuvalanmış)

my_struct=AttrDict ({
    'com1':AttrDict ({
        'inst':[0x05],
        'numbytes':2,
        'canpayload':False,
        'payload':None
    })
})

Daha sonra my_struct içindeki değerlere şu şekilde erişebilirsiniz:

print(my_struct.com1.inst)

=>[5]


0

NamedTuple rahattır. ancak performansı ve depolamayı kimse paylaşmıyor.

from typing import NamedTuple
import guppy  # pip install guppy
import timeit


class User:
    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserSlot:
    __slots__ = ('name', 'uid')

    def __init__(self, name: str, uid: int):
        self.name = name
        self.uid = uid


class UserTuple(NamedTuple):
    # __slots__ = ()  # AttributeError: Cannot overwrite NamedTuple attribute __slots__
    name: str
    uid: int


def get_fn(obj, attr_name: str):
    def get():
        getattr(obj, attr_name)
    return get
if 'memory test':
    obj = [User('Carson', 1) for _ in range(1000000)]      # Cumulative: 189138883
    obj_slot = [UserSlot('Carson', 1) for _ in range(1000000)]          # 77718299  <-- winner
    obj_namedtuple = [UserTuple('Carson', 1) for _ in range(1000000)]   # 85718297
    print(guppy.hpy().heap())  # Run this function individually. 
    """
    Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 1000000    24 112000000 34 112000000  34 dict of __main__.User
     1 1000000    24 64000000  19 176000000  53 __main__.UserTuple
     2 1000000    24 56000000  17 232000000  70 __main__.User
     3 1000000    24 56000000  17 288000000  87 __main__.UserSlot
     ...
    """

if 'performance test':
    obj = User('Carson', 1)
    obj_slot = UserSlot('Carson', 1)
    obj_tuple = UserTuple('Carson', 1)

    time_normal = min(timeit.repeat(get_fn(obj, 'name'), repeat=20))
    print(time_normal)  # 0.12550550000000005

    time_slot = min(timeit.repeat(get_fn(obj_slot, 'name'), repeat=20))
    print(time_slot)  # 0.1368690000000008

    time_tuple = min(timeit.repeat(get_fn(obj_tuple, 'name'), repeat=20))
    print(time_tuple)  # 0.16006120000000124

    print(time_tuple/time_slot)  # 1.1694481584580898  # The slot is almost 17% faster than NamedTuple on Windows. (Python 3.7.7)

Kullanmıyorsanız __dict__, lütfen __slots__(daha yüksek performans ve depolama alanı) ve NamedTuple(okuma ve kullanım için temizle ) arasında seçim yapın

Daha fazla bilgi almak için bu bağlantıyı ( yuvaların kullanımı ) inceleyebilirsiniz __slots__.

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.