Bagaimana cara menambahkan properti ke kelas secara dinamis?

216

Tujuannya adalah untuk membuat kelas tiruan yang berperilaku seperti resultset db.

Jadi misalnya, jika kueri basis data kembali, menggunakan ekspresi dict {'ab':100, 'cd':200},, maka saya ingin melihat:

>>> dummy.ab
100

Pada awalnya saya pikir mungkin saya bisa melakukannya dengan cara ini:

ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
    def __init__(self, ks, vs):
        for i, k in enumerate(ks):
            self[k] = vs[i]
            setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))

    def fn_readonly(self, v)
        raise "It is ready only"

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

tetapi c.abmengembalikan objek properti sebagai gantinya.

Mengganti setattrsaluran dengan k = property(lambda x: vs[i])tidak ada gunanya sama sekali.

Jadi apa cara yang tepat untuk membuat properti instance saat runtime?

PS Saya mengetahui adanya alternatif yang disajikan dalam Bagaimana __getattribute__metode yang digunakan?

Anthony Kong
sumber
2
Ada beberapa kesalahan ketik dalam kode Anda: definisi fn_readonly membutuhkan a :dan __init__referensi self.fn_readyonly.
mhawke
Kamu benar. Saya menambahkan bahwa fungsi setter di menit terakhir untuk menggarisbawahi alasan membuat properti di runtime.
Anthony Kong
Masalah utama yang saya miliki dengan membuat properti pada inisialisasi adalah bahwa, dalam beberapa kasus jika saya memanggil pembantu dengan cepat setelah itu, atau ada masalah, saya akan mendapatkan kesalahan bahwa mereka tidak ada terlepas dari kenyataan yang mereka lakukan. Dalam solusi saya di bawah ini, saya membuat 2 kelas. Satu sebagai Basis / Orang Tua (yang saya coba cari solusi untuk menghindari), dan objek utama, yang memperluas Basis / Orang Tua. Kemudian, di objek utama, tanpa menginisialisasi, saya memanggil pencipta AccessorFunc saya yang menciptakan properti, fungsi pembantu, dan banyak lagi.
Acecool
yaitu: class ExampleBase: pass; Contoh kelas (ExampleBase): __x = Accessor (ExampleBase, 'x', 'X', 123); --- yang akan membuat properti di bawah x dan fungsi bernama menggunakan X jadi GetX, SetX, dan banyak lagi ... dan .x, ._x, dan .__ x untuk properti. Jadi .x adalah properti itu sendiri untuk melewati data (mendapatkan / pengaturan melalui self.x = 123; atau self.x ke output). Saya menggunakan self._x untuk data RAW yang disimpan sehingga dapat dengan mudah diakses karena saya juga mengizinkan nilai-nilai default untuk ditugaskan, tanpa mengaturnya dalam data yang disimpan. jadi _x bisa berupa None dan .x bisa mengembalikan 123. dan .__ x ditautkan ke Accessor
Acecool
Berikut ini tautan ke versi dasar yang membuat properti dinamis, dan fungsi dinamis - file memiliki banyak tautan ke versi lain. Salah satunya adalah sistem AccessorFunc menggunakan fungsi untuk membuat pembantu (satu untuk fungsi, satu untuk properti, satu untuk keduanya sebagai elemen individu - sehingga tidak menggunakan pemendekan kode dalam apa pun di file itu). Jika ada yang hilang, salah satu file lain memilikinya: dropbox.com/s/phnnuavssmzeqrr/dynamic_properties_simple.py?dl=0
Acecool

Jawaban:

334

Saya kira saya harus memperluas jawaban ini, sekarang saya lebih tua dan lebih bijaksana dan tahu apa yang terjadi. Lebih baik terlambat daripada tidak sama sekali.

Anda bisa menambahkan properti ke kelas secara dinamis. Tapi itulah intinya: Anda harus menambahkannya ke kelas .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A propertysebenarnya adalah implementasi sederhana dari hal yang disebut deskriptor . Ini adalah objek yang menyediakan penanganan kustom untuk atribut yang diberikan, pada kelas tertentu . Agak seperti cara untuk faktor ifpohon besar keluar __getattribute__.

Ketika saya meminta foo.bdalam contoh di atas, Python melihat bahwa bdidefinisikan pada alat kelas protokol deskriptor -yang hanya berarti itu sebuah objek dengan __get__, __set__atau __delete__metode. Deskriptor mengklaim bertanggung jawab untuk menangani atribut itu, sehingga Python memanggil Foo.b.__get__(foo, Foo), dan nilai pengembalian dikirimkan kembali kepada Anda sebagai nilai atribut. Dalam kasus property, masing-masing metode ini hanya menyebut fget, fsetatau fdelAnda lulus ke propertykonstruktor.

Deskriptor adalah cara Python untuk mengekspos plumbing dari seluruh implementasi OO-nya. Bahkan, ada jenis deskriptor lain yang bahkan lebih umum daripada property.

>>> class Foo(object):
...     def bar(self):
...         pass
... 
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>

Metode sederhana hanyalah jenis deskriptor lainnya. Itu __get__paku pada instance panggilan sebagai argumen pertama; pada dasarnya, ia melakukan ini:

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

Ngomong-ngomong, saya menduga inilah sebabnya deskriptor hanya bekerja di kelas: mereka adalah formalisasi dari hal-hal yang memberdayakan kelas di tempat pertama. Mereka bahkan merupakan perkecualian dari aturan: Anda jelas dapat menetapkan deskriptor untuk suatu kelas, dan kelas itu sendiri adalah contoh dari type! Bahkan, mencoba membaca Foo.bpanggilan diam property.__get__; itu hanya idiomatik untuk deskriptor untuk mengembalikan diri ketika diakses sebagai atribut kelas.

Saya pikir itu sangat keren bahwa hampir semua sistem OO Python dapat diekspresikan dalam Python. :)

Oh, dan saya menulis posting blog bertele - tele tentang deskriptor beberapa waktu lalu jika Anda tertarik.

Eevee
sumber
35
Tidak perlu menambahkan metode add_property. setattr (Foo, 'name', property (func))
Courtney D
8
"Tapi itu maksudnya ..." hanya menyelamatkan saya beberapa jam kerja. Terima kasih.
Matt Howell
2
Jika Anda ingin mendefinisikan properti pada satu instance, Anda dapat membuat kelas saat runtime dan memodifikasi __class__ .
Wilfred Hughes
1
bagaimana dengan @ myproperty.setter? Bagaimana cara menambahkannya secara dinamis?
LRMAAX
Anda tidak perlu menambahkan properti ke objek yang diinisialisasi. Melakukannya mungkin berarti itu hanya menempel pada instance tetapi saya harus mengeceknya. Saya tahu saya mengalami masalah yang sama di mana properti dinamis saya adalah contoh saja, saya juga berakhir dengan setup statis dan yang saya inginkan adalah ke objek sehingga Inisialisasi masa depan akan memanfaatkannya. Posting saya di bawah ini dan itu menciptakan fungsi pembantu, dan cara mudah untuk mengakses semuanya dengan mudah. .x untuk properti, ._x untuk data mentah yang digunakan pengambil / penyetel (yang bisa berupa Tidak Ada), dan .__ x untuk objek accessor.
Acecool
57

Tujuannya adalah untuk membuat kelas tiruan yang berperilaku seperti resultset db.

Jadi yang Anda inginkan adalah kamus tempat Anda dapat mengeja ['b'] sebagai ab?

Itu mudah:

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__
bobince
sumber
1
dalam pengaturan yang lebih umum, ini melayani tujuan terbatas. jika dict memiliki hierarki bertingkat, seperti d = {'a1': {'b': 'c'}, 'a2': ...}, maka selagi Anda dapat melakukan d.a1 atau d.a2, Anda dapat ' t do d.a1.b
Shreyas
1
Satu hal yang perlu diingat adalah bahwa ini memungkinkan pengaturan nilai atribut untuk atribut dengan nama yang sama dengan metode dict atau atribut, tetapi tidak memungkinkan mengambil nilai-nilai dengan cara yang sama lagi: d.items = 1, d.itemskembali <built-in method items of atdict object at ...>. Anda masih bisa melakukan d["items"]atau menggunakan __getattribute__bukan __getattr__, tetapi ini mencegah menggunakan sebagian besar metode dikt.
Marcono1234
Cukup gunakan perpustakaan munch ! (fork of bunch)
Brian Peterson
38

Tampaknya Anda bisa menyelesaikan masalah ini lebih mudah dengan namedtuple, karena Anda tahu seluruh daftar bidang sebelumnya.

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

Jika Anda benar-benar perlu menulis setter Anda sendiri, Anda harus melakukan metaprogramming di tingkat kelas; property()tidak berfungsi pada instance.

Eevee
sumber
Ide yang hebat. Sayangnya saya terjebak dengan python 2.4 saat ini.
Anthony Kong
2
Pria yang menulis namedtuplepantas mendapatkan hadiah karena membuatnya halus dan elegan untuk menjadi prinsip berorientasi objek yang setia.
Keith Pinson
4
Maaf, paling banter, jawaban ini hanya berlaku untuk kasus khusus di mana satu kelas wanteda yang hanya terdiri dari atribut read-only yang sudah diketahui sebelumnya. Dengan kata lain saya tidak berpikir itu membahas pertanyaan yang lebih luas tentang bagaimana menambahkan properti umum - bukan hanya yang hanya baca - ke kelas saat runtime (juga versi saat ini dari jawaban "tambahan" lainnya juga diposting oleh penulis).
martineau
@martineau jadi ... berikan lebih banyak argumen property()? tidak ada jawaban apa pun yang khusus untuk properti hanya baca.
Eevee
32

Anda tidak perlu menggunakan properti untuk itu. Timpa saja __setattr__untuk membuatnya hanya baca.

class C(object):
    def __init__(self, keys, values):
        for (key, value) in zip(keys, values):
            self.__dict__[key] = value

    def __setattr__(self, name, value):
        raise Exception("It is read only!")

Tada.

>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in __setattr__
Exception: It is read only!
Ryan
sumber
9

Bagaimana cara menambahkan properti ke kelas python secara dinamis?

Katakanlah Anda memiliki objek yang ingin Anda tambahkan properti. Biasanya, saya ingin menggunakan properti ketika saya harus mulai mengelola akses ke atribut dalam kode yang memiliki penggunaan hilir, sehingga saya dapat mempertahankan API yang konsisten. Sekarang saya biasanya akan menambahkannya ke kode sumber di mana objek didefinisikan, tetapi mari kita asumsikan Anda tidak memiliki akses itu, atau Anda harus benar-benar secara dinamis memilih fungsi Anda secara terprogram.

Buat kelas

Menggunakan contoh yang didasarkan pada dokumentasi untukproperty , mari kita buat kelas objek dengan atribut "tersembunyi" dan buat instance darinya:

class C(object):
    '''basic class'''
    _x = None

o = C()

Dengan Python, kami berharap ada satu cara yang jelas dalam melakukan sesuatu. Namun, dalam hal ini, saya akan menunjukkan dua cara: dengan notasi dekorator, dan tanpa. Pertama, tanpa notasi dekorator. Ini mungkin lebih berguna untuk penugasan dinamis dari getter, setter, atau deleter.

Dynamic (alias Monkey Patching)

Mari kita buat beberapa untuk kelas kita:

def getx(self):
    return self._x

def setx(self, value):
    self._x = value

def delx(self):
    del self._x

Dan sekarang kami menetapkan ini ke properti. Perhatikan bahwa kita dapat memilih fungsi kita secara terprogram di sini, menjawab pertanyaan dinamis:

C.x = property(getx, setx, delx, "I'm the 'x' property.")

Dan penggunaan:

>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:

    I'm the 'x' property.

Dekorator

Kita dapat melakukan hal yang sama seperti yang kita lakukan di atas dengan notasi dekorator, tetapi dalam kasus ini, kita harus memberi nama metode semua nama yang sama (dan saya akan merekomendasikan menjaganya tetap sama dengan atribut), sehingga tugas terprogram tidak sepele seperti itu menggunakan metode di atas:

@property
def x(self):
    '''I'm the 'x' property.'''
    return self._x

@x.setter
def x(self, value):
    self._x = value

@x.deleter
def x(self):
    del self._x

Dan tetapkan objek properti dengan setter dan delta yang disediakan untuk kelas:

C.x = x

Dan penggunaan:

>>> help(C.x)
Help on property:

    I'm the 'x' property.

>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
Aaron Hall
sumber
5

Saya mengajukan pertanyaan serupa pada posting Stack Overflow ini untuk membuat pabrik kelas yang membuat tipe sederhana. Hasilnya adalah jawaban ini yang memiliki versi pabrik kelas yang berfungsi. Berikut cuplikan jawabannya:

def Struct(*args, **kwargs):
    def init(self, *iargs, **ikwargs):
        for k,v in kwargs.items():
            setattr(self, k, v)
        for i in range(len(iargs)):
            setattr(self, args[i], iargs[i])
        for k,v in ikwargs.items():
            setattr(self, k, v)

    name = kwargs.pop("name", "MyStruct")
    kwargs.update(dict((k, None) for k in args))
    return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})

>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>

Anda dapat menggunakan beberapa variasi ini untuk membuat nilai default yang merupakan tujuan Anda (ada juga jawaban dalam pertanyaan yang berhubungan dengan ini).

kjfletch
sumber
4

Tidak yakin apakah saya benar-benar memahami pertanyaan, tetapi Anda dapat mengubah properti instance saat runtime dengan bawaan __dict__kelas Anda:

class C(object):
    def __init__(self, ks, vs):
        self.__dict__ = dict(zip(ks, vs))


if __name__ == "__main__":
    ks = ['ab', 'cd']
    vs = [12, 34]
    c = C(ks, vs)
    print(c.ab) # 12
Crescent Fresh
sumber
Intinya pertanyaan saya adalah mencari tahu apakah mungkin untuk membuat properti baru di runtime. Konsensus tampaknya negatif. Saran Anda tentu sederhana dan praktis. (Sama dengan jawaban lain yang menggunakan dict )
Anthony Kong
Jawaban sederhana juga akan:self.__dict__[key] = value
Allan Karlson
4

Bagi mereka yang datang dari mesin pencari, berikut adalah dua hal yang saya cari ketika berbicara tentang properti dinamis :

class Foo:
    def __init__(self):
        # we can dynamically have access to the properties dict using __dict__
        self.__dict__['foo'] = 'bar'

assert Foo().foo == 'bar'


# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
    def __init__(self):
        self._data = {}
    def __getattr__(self, key):
        return self._data[key]
    def __setattr__(self, key, value):
        self._data[key] = value

bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'

__dict__ bagus jika Anda ingin menempatkan properti yang dibuat secara dinamis. __getattr__Adalah baik untuk hanya melakukan sesuatu ketika nilai dibutuhkan, seperti permintaan basis data. Combo set / get bagus untuk menyederhanakan akses ke data yang disimpan di kelas (seperti pada contoh di atas).

Jika Anda hanya menginginkan satu properti dinamis, lihat fungsi properti () bawaan.

tleb
sumber
4

Anda tidak dapat menambahkan yang baru property()ke instance saat runtime, karena properti adalah deskriptor data. Sebaliknya, Anda harus secara dinamis membuat kelas baru, atau kelebihan __getattribute__untuk memproses deskriptor data pada instance.

Alex Gaynor
sumber
Ini salah. Anda bisa menambahkan properti ke kelas lalu mengaksesnya dari metode.
Ahmed
2

Cara terbaik untuk mencapai adalah dengan mendefinisikan __slots__. Dengan begitu instance Anda tidak dapat memiliki atribut baru.

ks = ['ab', 'cd']
vs = [12, 34]

class C(dict):
    __slots__ = []
    def __init__(self, ks, vs): self.update(zip(ks, vs))
    def __getattr__(self, key): return self[key]

if __name__ == "__main__":
    c = C(ks, vs)
    print c.ab

Cetakan itu 12

    c.ab = 33

Itu memberi: AttributeError: 'C' object has no attribute 'ab'

nosklo
sumber
2

Contoh lain cara mencapai efek yang diinginkan

class Foo(object):

    _bar = None

    @property
    def bar(self):
        return self._bar

    @bar.setter
    def bar(self, value):
        self._bar = value

    def __init__(self, dyn_property_name):
        setattr(Foo, dyn_property_name, Foo.bar)

Jadi sekarang kita bisa melakukan hal-hal seperti:

>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
lehins
sumber
2

Berikut ini solusinya:

  • Mengizinkan menentukan nama properti sebagai string , sehingga mereka dapat berasal dari beberapa sumber data luar dan bukan semua yang terdaftar dalam program Anda.
  • Menambahkan properti ketika kelas didefinisikan , bukan setiap kali sebuah objek dibuat.

Setelah kelas telah ditentukan, Anda cukup melakukan ini untuk menambahkan properti ke dalamnya secara dinamis:

setattr(SomeClass, 'propertyName', property(getter, setter))

Berikut ini adalah contoh lengkap, diuji dengan Python 3:

#!/usr/bin/env python3

class Foo():
  pass

def get_x(self):
  return 3

def set_x(self, value):
  print("set x on %s to %d" % (self, value))

setattr(Foo, 'x', property(get_x, set_x))

foo1 = Foo()
foo1.x = 12
print(foo1.x)
David Grayson
sumber
1

Anda dapat menggunakan kode berikut untuk memperbarui atribut kelas menggunakan objek kamus:

class ExampleClass():
    def __init__(self, argv):
        for key, val in argv.items():
            self.__dict__[key] = val

if __name__ == '__main__':
    argv = {'intro': 'Hello World!'}
    instance = ExampleClass(argv)
    print instance.intro
Anthony Holloman
sumber
1

Ini sedikit berbeda dari yang diinginkan OP, tetapi saya mengguncang otak saya sampai saya mendapatkan solusi yang berfungsi, jadi saya menempatkan di sini untuk orang berikutnya

Saya membutuhkan cara untuk menentukan setter dinamis dan getter.

class X:
    def __init__(self, a=0, b=0, c=0):
        self.a = a
        self.b = b
        self.c = c

    @classmethod
    def _make_properties(cls, field_name, inc):
        _inc = inc

        def _get_properties(self):
            if not hasattr(self, '_%s_inc' % field_name):
                setattr(self, '_%s_inc' % field_name, _inc)
                inc = _inc
            else:
                inc = getattr(self, '_%s_inc' % field_name)

            return getattr(self, field_name) + inc

        def _set_properties(self, value):
            setattr(self, '_%s_inc' % field_name, value)

        return property(_get_properties, _set_properties)

Saya tahu bidang saya sebelumnya jadi saya akan membuat properti saya. CATATAN: Anda tidak dapat melakukan instance PER ini, properti ini akan ada di kelas !!!

for inc, field in enumerate(['a', 'b', 'c']):
    setattr(X, '%s_summed' % field, X._make_properties(field, inc))

Mari kita uji semuanya sekarang ..

x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0

assert x.a_summed == 0  # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1  # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2  # enumerate() set inc to 2 + 0 = 2

# we set the variables to something
x.a = 1
x.b = 2
x.c = 3

assert x.a_summed == 1  # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3  # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5  # enumerate() set inc to 2 + 3 = 5

# we're changing the inc now
x.a_summed = 1 
x.b_summed = 3 
x.c_summed = 5

assert x.a_summed == 2  # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5  # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8  # we set inc to 5 + the property was 3 = 8

Apakah ini membingungkan? Ya, maaf saya tidak bisa memberikan contoh dunia nyata yang berarti. Juga, ini bukan untuk yang berhati ringan.

Javier Buzzi
sumber
Jika saya ingat dengan benar, saya memang menemukan cara selama semua pengujian saya untuk membuat properti tipe STATIC / g / setter ditambahkan secara dinamis. Saya harus melalui semua yang sebelumnya - tetapi bisa menambahkan sesuatu yang dibagikan di antara semua contoh pasti mungkin. Adapun membuat pada proses per-instance ... Saya cukup yakin Anda bisa sehingga satu contoh memiliki sesuatu yang lain tidak. Saya harus memverifikasi, tetapi saya juga mengalami hal seperti ini (dalam upaya pertama saya, saya membuat kesalahan yang menyebabkan fungsi dibuat, tetapi tidak semua instance memilikinya karena cacat)
Acecool
Juga, setiap solusi yang mungkin diterima karena ini adalah repo pengetahuan. Menyenangkan juga melihat berbagai cara orang yang berbeda menciptakan solusi untuk suatu masalah. Solusi saya melakukan BANYAK, Anda mengurangi ini menjadi sesuatu yang lebih mudah untuk dibagikan. Saya melakukan varian yang lebih kecil juga - seharusnya ada di suatu tempat dalam topik ini - dan saya baru sadar itu bukan yang saya posting: -) ...
Acecool
0

Ini tampaknya berhasil (tetapi lihat di bawah):

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
        self.__dict__.update(self)
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)

Jika Anda membutuhkan perilaku yang lebih kompleks, silakan edit jawaban Anda.

sunting

Berikut ini mungkin akan lebih hemat memori untuk kumpulan data besar:

class data(dict,object):
    def __init__(self,*args,**argd):
        dict.__init__(self,*args,**argd)
    def __getattr__(self,name):
        return self[name]
    def __setattr__(self,name,value):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
    def __delattr__(self,name):
        raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
David X
sumber
0

Untuk menjawab dorongan utama pertanyaan Anda, Anda ingin atribut read-only dari dict sebagai sumber data yang tidak dapat diubah:

Tujuannya adalah untuk membuat kelas tiruan yang berperilaku seperti resultset db.

Jadi misalnya, jika kueri basis data kembali, menggunakan ekspresi dict {'ab':100, 'cd':200},, maka saya akan melihatnya

>>> dummy.ab
100

Saya akan menunjukkan cara menggunakan namedtupledari collectionsmodul untuk mencapai ini:

import collections

data = {'ab':100, 'cd':200}

def maketuple(d):
    '''given a dict, return a namedtuple'''
    Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
    return Tup(**d)

dummy = maketuple(data)
dummy.ab

kembali 100

Aaron Hall
sumber
0
class atdict(dict):
  def __init__(self, value, **kwargs):
    super().__init__(**kwargs)
    self.__dict = value

  def __getattr__(self, name):
    for key in self.__dict:
      if type(self.__dict[key]) is list:
        for idx, item in enumerate(self.__dict[key]):
          if type(item) is dict:
            self.__dict[key][idx] = atdict(item)
      if type(self.__dict[key]) is dict:
        self.__dict[key] = atdict(self.__dict[key])
    return self.__dict[name]



d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})

print(d1.a.b[0].c)

Dan hasilnya adalah:

>> 1
Serhii Khachko
sumber
0

Memperluas ide dari kjfletch

# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.

def Struct(*args, **kwargs):
    FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
                 types.FunctionType, types.MethodType)
    def init(self, *iargs, **ikwargs):
        """Asume that unamed args are placed in the same order than
        astuple() yields (currently alphabetic order)
        """
        kw = list(self.__slots__)

        # set the unnamed args
        for i in range(len(iargs)):
            k = kw.pop(0)
            setattr(self, k, iargs[i])

        # set the named args
        for k, v in ikwargs.items():
            setattr(self, k, v)
            kw.remove(k)

        # set default values
        for k in kw:
            v = kwargs[k]
            if isinstance(v, FUNCTIONS):
                v = v()
            setattr(self, k, v)

    def astuple(self):
        return tuple([getattr(self, k) for k in self.__slots__])

    def __str__(self):
        data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
        return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))

    def __repr__(self):
        return str(self)

    def __eq__(self, other):
        return self.astuple() == other.astuple()

    name = kwargs.pop("__name__", "MyStruct")
    slots = list(args)
    slots.extend(kwargs.keys())
    # set non-specific default values to None
    kwargs.update(dict((k, None) for k in args))

    return type(name, (object,), {
        '__init__': init,
        '__slots__': tuple(slots),
        'astuple': astuple,
        '__str__': __str__,
        '__repr__': __repr__,
        '__eq__': __eq__,
    })


Event = Struct('user', 'cmd', \
               'arg1', 'arg2',  \
               date=time.time, \
               __name__='Event')

aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()

bb = Event(*raw)
print(bb)

if aa == bb:
    print('Are equals')

cc = Event(cmd='foo')
print(cc)

Keluaran:

<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
asterio gonzalez
sumber
0

Meskipun banyak jawaban diberikan, saya tidak dapat menemukan jawaban yang saya sukai. Saya menemukan solusi saya sendiri yang membuat propertybekerja untuk kasus yang dinamis. Sumber untuk menjawab pertanyaan awal:

#!/usr/local/bin/python3

INITS = { 'ab': 100, 'cd': 200 }

class DP(dict):
  def __init__(self):
    super().__init__()
    for k,v in INITS.items():
        self[k] = v 

def _dict_set(dp, key, value):
  dp[key] = value

for item in INITS.keys():
  setattr(
    DP,
    item,
    lambda key: property(
      lambda self: self[key], lambda self, value: _dict_set(self, key, value)
    )(item)
  )

a = DP()
print(a)  # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
Rob L
sumber
0

Sesuatu yang bekerja untuk saya adalah ini:

class C:
    def __init__(self):
        self._x=None

    def g(self):
        return self._x

    def s(self, x):
        self._x = x

    def d(self):
        del self._x

    def s2(self,x):
        self._x=x+x

    x=property(g,s,d)


c = C()
c.x="a"
print(c.x)

C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)

Keluaran

a
aa
prosti
sumber
-1

Saya baru-baru ini mengalami masalah yang sama, solusi yang saya gunakan __getattr__dan __setattr__untuk properti yang ingin saya tangani, semua yang lain diteruskan ke aslinya.

class C(object):
    def __init__(self, properties):
        self.existing = "Still Here"
        self.properties = properties

    def __getattr__(self, name):
        if "properties" in self.__dict__ and name in self.properties:
            return self.properties[name] # Or call a function, etc
        return self.__dict__[name]

    def __setattr__(self, name, value):
        if "properties" in self.__dict__ and name in self.properties:
            self.properties[name] = value
        else:
            self.__dict__[name] = value

if __name__ == "__main__":
    my_properties = {'a':1, 'b':2, 'c':3}
    c = C(my_properties)
    assert c.a == 1
    assert c.existing == "Still Here"
    c.b = 10
    assert c.properties['b'] == 10
teeks99
sumber
Saya melihat ini, tetapi Anda secara teknis akan melalui daftar di pengambil dan setter Anda. Karena itu, setiap panggilan akan lebih lambat karena Anda mencari dari daftar, pertama, daripada mengaksesnya secara langsung. Kecuali jika Python otomatis memetakannya untuk Anda; mungkin, tapi saya belum membuat tolok ukur ini untuk mengetahui dengan pasti, tetapi ini menjadi perhatian saya sebelum saya mencobanya. Kedua, dengan melakukan ini, Anda harus mendefinisikan pembantu dengan cara lain. Anda juga tidak dapat mengunci tipe data dan / atau nilai tanpa berakhir dengan kamus besar, atau banyak baris tambahan.
Acecool
yaitu: Saya harus membuat kelas dasar yang saya gunakan untuk memperluas semua anak saya yang menggunakan sistem, ATAU saya harus menambahkan fungsi sulap s / getattr ke semua dan menduplikasi sistem setiap saat. Deklarasi properti juga berarti Anda harus mengaturnya satu arah, dan jika Anda menginginkan salah satu dukungan tambahan, seperti yang telah saya sebutkan seperti tipe data dan atau nilai perlindungan untuk memungkinkan atau mencegah data ditugaskan dan pembantu lainnya , maka Anda harus kode mereka. Memang, Anda bisa membuat sistem serupa dalam perilaku tetapi berakhir di mana Anda menyatakan sedikit berbeda dan bulkier.
Acecool
-1

Berikut adalah contoh sederhana untuk membuat objek properti secara terprogram.

#!/usr/bin/python3

class Counter:
    def __init__(self):
        cls = self.__class__
        self._count = 0
        cls.count = self.count_ref()

    def count_get(self):
        print(f'count_get: {self._count}')
        return self._count

    def count_set(self, value):
        self._count = value
        print(f'count_set: {self._count}')

    def count_del(self):
        print(f'count_del: {self._count}')

    def count_ref(self):
        cls = self.__class__
        return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)

counter = Counter()

counter.count
for i in range(5):
    counter.count = i
del counter.count

'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
Suhas Siddu
sumber
-2

Satu-satunya cara untuk melampirkan properti secara dinamis adalah dengan membuat kelas baru dan instansnya dengan properti baru Anda.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
M. Utku ALTINKAYA
sumber
1
ini tampaknya tidak berfungsi. itu akan menetapkan hasil dari properti, bukan properti itu sendiri.
mjallday
Ini salah. Saya melampirkan properti dinamis dengan sistem saya tanpa harus menginisialisasi kelas. Inisialisasi menjadi x = Contoh (), lalu tambahkan properti ke x.
Acecool
Jika Anda melihat kode saya, Anda akan melihat bahwa saya menggunakan class ExampleBase: pass, lalu class Example (ExampleBase): ... maka saya melampirkan properti ke ExampleBase, karena nama itu ada, dan karena Contoh memanjang dari itu, ia memiliki akses ke semuanya. Saya menggunakan var __ untuk pembantu accessor untuk dapat memiliki akses langsung ke objek accessor, saya menggunakan _ untuk data yang disimpan (mentah) yang bisa menjadi Tidak ada, dan tidak ada garis bawah untuk properti aktual yang melewati pengambil. Saya dapat memanggil fungsi pengambil menggunakan fungsi yang ditambahkan secara dinamis, atau menggunakan properti. Semua tanpa init'd pertama.
Acecool
Catatan: Saya memang menyebutkannya - tetapi definisi saya untuk definisi berarti referensi ada di namespace - yaitu: kelas Contoh (Obyek): lulus ... Itu ada, tetapi belum diinisialisasi. Inisialisasi berarti blah = Contoh (); sekarang objek telah 'digandakan' dan dibangun, kemudian disimpan sebagai referensi dalam bla. --- Jika Anda melakukan ini, maka fungsi / properti yang ditambahkan secara dinamis seharusnya hanya dalam contoh - masalah yang saya miliki dengan ini bahkan jika fungsi ada, ada kasus di mana saya mendapat kesalahan mengatakan mereka tidak melakukannya. Entah kesalahan pemblokiran menghentikan pembuatan, atau eksekusi async.
Acecool
Beberapa catatan lainnya: Dimungkinkan untuk membuat properti secara dinamis, dan berfungsi dengan cara yang hanya ada per instance. Anda juga dapat membuatnya sehingga mereka ada untuk objek (yang Anda inginkan dalam kebanyakan kasus). Dan ada kasus di mana elemen yang ditambahkan 'statis', yaitu referensi yang sama, dan nilai yang dikembalikan dibagikan di semua contoh - jika Anda memperbarui dalam satu area, semua mendapatkan yang sama ..
Acecool
-6

Banyak jawaban yang disediakan membutuhkan begitu banyak baris per properti, yaitu / dan / atau - apa yang saya anggap implementasi yang jelek atau membosankan karena diperlukan pengulangan untuk banyak properti, dll. Saya lebih suka terus merebus / menyederhanakannya sampai mereka tidak dapat disederhanakan lagi atau sampai tidak ada gunanya untuk melakukannya.

Singkatnya: dalam pekerjaan yang diselesaikan, jika saya mengulangi 2 baris kode, saya biasanya mengubahnya menjadi fungsi pembantu baris tunggal, dan seterusnya ... Saya menyederhanakan matematika atau argumen aneh seperti (start_x, start_y, end_x, end_y) ke (x, y, w, h) yaitu x, y, x + w, y + h (kadang-kadang membutuhkan min / max atau jika w / h negatif dan implementasinya tidak menyukainya, saya akan kurangi dari x / y dan abs w / h dll.).

Mengesampingkan pengambil / penetapan internal adalah cara yang baik untuk pergi, tetapi masalahnya adalah Anda harus melakukan itu untuk setiap kelas, atau induk kelas ke basis itu ... Ini tidak bekerja untuk saya karena saya lebih suka menjadi bebas memilih anak / orang tua untuk warisan, simpul anak, dll.

Saya telah menciptakan solusi yang menjawab pertanyaan tanpa menggunakan tipe data Dict untuk memasok data karena saya menemukan bahwa menjadi membosankan untuk memasukkan data, dll ...

Solusi saya mengharuskan Anda untuk menambahkan 2 baris tambahan di atas kelas Anda untuk membuat kelas dasar untuk kelas yang ingin Anda tambahkan properti, kemudian 1 baris per dan Anda memiliki opsi untuk menambahkan panggilan balik untuk mengontrol data, memberi tahu Anda ketika data berubah , batasi data yang dapat diatur berdasarkan nilai dan / atau tipe data, dan banyak lagi.

Anda juga memiliki opsi untuk menggunakan _object.x, _object.x = value, _object.GetX (), _object.SetX (value) dan mereka ditangani secara setara.

Selain itu, nilai adalah satu-satunya data non-statis yang ditugaskan ke instance kelas, tetapi properti aktual ditugaskan ke kelas yang berarti hal-hal yang tidak ingin Anda ulangi, tidak perlu diulang ... Anda dapat menetapkan nilai default sehingga pengambil tidak perlu setiap kali, meskipun ada opsi untuk mengganti nilai default default, dan ada opsi lain sehingga pengambil mengembalikan nilai yang disimpan mentah dengan menimpa pengembalian default (catatan: metode ini berarti nilai mentah hanya diberikan ketika nilai diberikan, jika tidak, tidak ada - ketika nilai diatur ulang, maka itu menetapkan Tidak ada, dll.)

Ada banyak fungsi pembantu juga - properti pertama yang ditambahkan menambahkan 2 atau lebih pembantu ke kelas untuk mereferensikan nilai instance ... Mereka adalah ResetAccessors (_key, ..) vararg diulangi (semua dapat diulang menggunakan arg yang disebut pertama) ) dan SetAccessors (_key, _value) dengan opsi lebih banyak ditambahkan ke kelas utama untuk membantu dalam efisiensi - yang direncanakan adalah: cara untuk mengelompokkan pengakses bersama, jadi jika Anda cenderung mengatur ulang beberapa waktu, setiap kali , Anda dapat menetapkannya ke grup dan mengatur ulang grup alih-alih mengulangi tombol yang disebutkan setiap kali, dan lainnya.

Instance / nilai tersimpan mentah disimpan di kelas., kelas. referensi Kelas Accessor yang memegang vars / nilai / fungsi statis untuk properti. _kelas. adalah properti itu sendiri yang dipanggil saat diakses melalui kelas instan selama pengaturan / mendapatkan, dll.

Accessor _class .__ menunjuk ke kelas, tetapi karena itu internal maka perlu ditugaskan di kelas yang mengapa saya memilih untuk menggunakan __Name = AccessorFunc (...) untuk menetapkannya, satu baris per properti dengan banyak opsional argumen untuk digunakan (menggunakan vararg yang dikunci karena lebih mudah dan lebih efisien untuk mengidentifikasi dan memelihara) ...

Saya juga membuat banyak fungsi, seperti yang disebutkan, beberapa di antaranya menggunakan informasi fungsi accessor sehingga tidak perlu dipanggil (karena sedikit merepotkan saat ini - saat ini Anda perlu menggunakan _class. .FunctionName (_class_instance , args) - Saya sempat menggunakan stack / trace untuk mengambil referensi instance untuk mengambil nilai dengan menambahkan fungsi-fungsi yang menjalankan bit maraton ini, atau dengan menambahkan accessors ke objek dan menggunakan self (dinamai ini untuk menunjukkan mereka Sedang untuk instance dan untuk mempertahankan akses ke diri, referensi kelas AccessorFunc, dan informasi lainnya dari dalam definisi fungsi).

Ini tidak cukup dilakukan, tetapi ini adalah pegangan kaki yang fantastis. Catatan: Jika Anda tidak menggunakan __Name = AccessorFunc (...) untuk membuat properti, Anda tidak akan memiliki akses ke kunci __ meskipun saya mendefinisikannya dalam fungsi init. Jika Anda melakukannya, maka tidak ada masalah.

Juga: Perhatikan bahwa Nama dan Kunci berbeda ... Nama itu 'formal', digunakan dalam Pembuatan Nama Fungsi, dan kuncinya adalah untuk penyimpanan data dan akses. yaitu _class.x di mana huruf kecil x adalah kunci, namanya akan menjadi huruf besar X sehingga GetX () adalah fungsi alih-alih Getx () yang terlihat sedikit aneh. ini memungkinkan self.x bekerja dan terlihat sesuai, tetapi juga memungkinkan GetX () dan terlihat sesuai.

Saya memiliki contoh kelas yang diatur dengan kunci / nama yang identik, dan berbeda untuk ditampilkan. banyak fungsi pembantu dibuat untuk menampilkan data (Catatan: Tidak semua ini selesai) sehingga Anda dapat melihat apa yang sedang terjadi.

Daftar fungsi saat ini menggunakan kunci: x, nama: X output sebagai:

Ini bukan daftar lengkap - ada beberapa yang belum membuatnya pada saat posting ...

_instance.SetAccessors( _key, _value [ , _key, _value ] .. )                   Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines.    In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. )                                 Instance Class Helper Function: Allows resetting many key stored values to None on a single line.                                           In short: Calls this.Reset<Name>() for each name provided.


Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.

this.GetX( _default_override = None, _ignore_defaults = False )                 GET:            Returns    IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None  .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE       100
this.GetXRaw( )                                                                 RAW:            Returns    STORED_VALUE                                                                                                     100
this.IsXSet( )                                                                  ISSET:          Returns    ( STORED_VALUE != None )                                                                                         True

this.GetXToString( )                                                            GETSTR:         Returns    str( GET )                                                                                                       100
this.GetXLen( _default_override = None, _ignore_defaults = False )              LEN:            Returns    len( GET )                                                                                                       3
this.GetXLenToString( _default_override = None, _ignore_defaults = False )      LENSTR:         Returns    str( len( GET ) )                                                                                                3
this.GetXDefaultValue( )                                                        DEFAULT:        Returns    DEFAULT_VALUE                                                                                                    1111

this.GetXAccessor( )                                                            ACCESSOR:       Returns    ACCESSOR_REF ( self.__<key> )                                                                                    [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848        Default: 1111       Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}     Allowed Values: None
this.GetXAllowedTypes( )                                                        ALLOWED_TYPES:  Returns    Allowed Data-Types                                                                                               {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( )                                                       ALLOWED_VALUES: Returns    Allowed Values                                                                                                   None

this.GetXHelpers( )                                                             HELPERS:        Returns    Helper Functions String List - ie what you're reading now...                                                     THESE ROWS OF TEXT
this.GetXKeyOutput( )                                                           Returns information about this Name / Key                                                                                                   ROWS OF TEXT
this.GetXGetterOutput( )                                                        Returns information about this Name / Key                                                                                                   ROWS OF TEXT

this.SetX( _value )                                                             SET:            STORED_VALUE Setter - ie Redirect to __<Key>.Set                                                                            N / A
this.ResetX( )                                                                  RESET:          Resets STORED_VALUE to None                                                                                                 N / A

this.HasXGetterPrefix( )                                                        Returns Whether or Not this key has a Getter Prefix...                                                                                      True
this.GetXGetterPrefix( )                                                        Returns Getter Prefix...                                                                                                                    Get

this.GetXName( )                                                                Returns Accessor Name - Typically Formal / Title-Case                                                                                       X
this.GetXKey( )                                                                 Returns Accessor Property Key - Typically Lower-Case                                                                                        x
this.GetXAccessorKey( )                                                         Returns Accessor Key - This is to access internal functions, and static data...                                                             __x
this.GetXDataKey( )                                                             Returns Accessor Data-Storage Key - This is the location where the class instance value is stored..                                         _x

Beberapa data yang dihasilkan adalah:

Ini untuk kelas baru yang dibuat menggunakan kelas Demo tanpa data yang diberikan selain nama (sehingga bisa menjadi output) yaitu _foo, nama variabel yang saya gunakan ...

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        1111                | _x:       None                     | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        2222                | _y:       None                     | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        3333                | _z:       None                     | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     <class 'int'>       | _Blah:    None                     | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    1                   | _Width:   None                     | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   0                   | _Height:  None                     | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    2                   | _Depth:   None                     | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |


this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       False     this.GetX( ):        1111                     this.GetXRaw( ):       None                     this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       4    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       False     this.GetY( ):        2222                     this.GetYRaw( ):       None                     this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       4    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       False     this.GetZ( ):        3333                     this.GetZRaw( ):       None                     this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       4    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    False     this.GetBlah( ):     <class 'int'>            this.GetBlahRaw( ):    None                     this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    13   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   False     this.GetWidth( ):    1                        this.GetWidthRaw( ):   None                     this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   1    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   False     this.GetDepth( ):    2                        this.GetDepthRaw( ):   None                     this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  False     this.GetHeight( ):   0                        this.GetHeightRaw( ):  None                     this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

Dan ini setelah menetapkan semua properti _foo (kecuali nama) nilai-nilai berikut dalam urutan yang sama: 'string', 1.0, True, 9, 10, False

this.IsNameSet( ):    True      this.GetName( ):     _foo                     this.GetNameRaw( ):    _foo                     this.GetNameDefaultValue( ):    AccessorFuncDemoClass    this.GetNameLen( ):    4    this.HasNameGetterPrefix( ):    <class 'str'>                                this.GetNameGetterPrefix( ):    None
this.IsXSet( ):       True      this.GetX( ):        10                       this.GetXRaw( ):       10                       this.GetXDefaultValue( ):       1111                     this.GetXLen( ):       2    this.HasXGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetXGetterPrefix( ):       None
this.IsYSet( ):       True      this.GetY( ):        10                       this.GetYRaw( ):       10                       this.GetYDefaultValue( ):       2222                     this.GetYLen( ):       2    this.HasYGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetYGetterPrefix( ):       None
this.IsZSet( ):       True      this.GetZ( ):        10                       this.GetZRaw( ):       10                       this.GetZDefaultValue( ):       3333                     this.GetZLen( ):       2    this.HasZGetterPrefix( ):       (<class 'int'>, <class 'float'>)             this.GetZGetterPrefix( ):       None
this.IsBlahSet( ):    True      this.GetBlah( ):     string Blah              this.GetBlahRaw( ):    string Blah              this.GetBlahDefaultValue( ):    <class 'int'>            this.GetBlahLen( ):    11   this.HasBlahGetterPrefix( ):    <class 'str'>                                this.GetBlahGetterPrefix( ):    None
this.IsWidthSet( ):   True      this.GetWidth( ):    False                    this.GetWidthRaw( ):   False                    this.GetWidthDefaultValue( ):   1                        this.GetWidthLen( ):   5    this.HasWidthGetterPrefix( ):   (<class 'int'>, <class 'bool'>)              this.GetWidthGetterPrefix( ):   None
this.IsDepthSet( ):   True      this.GetDepth( ):    9                        this.GetDepthRaw( ):   9                        this.GetDepthDefaultValue( ):   2                        this.GetDepthLen( ):   1    this.HasDepthGetterPrefix( ):   None                                         this.GetDepthGetterPrefix( ):   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ):  True      this.GetHeight( ):   9                        this.GetHeightRaw( ):  9                        this.GetHeightDefaultValue( ):  0                        this.GetHeightLen( ):  1    this.HasHeightGetterPrefix( ):  <class 'int'>                                this.GetHeightGetterPrefix( ):  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

_foo         --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016

    Key       Getter Value        | Raw Key   Raw / Stored Value       | Get Default Value             Default Value            | Get Allowed Types             Allowed Types                                                              | Get Allowed Values            Allowed Values                                                                                                                                                                                                                   |

    Name:     _foo                | _Name:    _foo                     | __Name.DefaultValue( ):       AccessorFuncDemoClass    | __Name.GetAllowedTypes( )     <class 'str'>                                                              | __Name.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    x:        10                  | _x:       10                       | __x.DefaultValue( ):          1111                     | __x.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __x.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    y:        10                  | _y:       10                       | __y.DefaultValue( ):          2222                     | __y.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __y.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    z:        10                  | _z:       10                       | __z.DefaultValue( ):          3333                     | __z.GetAllowedTypes( )        (<class 'int'>, <class 'float'>)                                           | __z.GetAllowedValues( )       Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Blah:     string Blah         | _Blah:    string Blah              | __Blah.DefaultValue( ):       <class 'int'>            | __Blah.GetAllowedTypes( )     <class 'str'>                                                              | __Blah.GetAllowedValues( )    Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Width:    False               | _Width:   False                    | __Width.DefaultValue( ):      1                        | __Width.GetAllowedTypes( )    (<class 'int'>, <class 'bool'>)                                            | __Width.GetAllowedValues( )   Saved Value Restrictions Levied by Data-Type                                                                                                                                                                                     |
    Height:   9                   | _Height:  9                        | __Height.DefaultValue( ):     0                        | __Height.GetAllowedTypes( )   <class 'int'>                                                              | __Height.GetAllowedValues( )  (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |
    Depth:    9                   | _Depth:   9                        | __Depth.DefaultValue( ):      2                        | __Depth.GetAllowedTypes( )    Saved Value Restricted to Authorized Values ONLY                           | __Depth.GetAllowedValues( )   (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)                                                                                                                                                                                                   |

Perhatikan bahwa karena tipe data atau batasan nilai terbatas, beberapa data tidak ditugaskan - ini adalah desain. Penyetel melarang tipe data atau nilai buruk ditugaskan, bahkan dari ditugaskan sebagai nilai default (kecuali Anda mengabaikan perilaku perlindungan nilai default)

Kode belum diposting di sini karena saya tidak punya ruang setelah contoh dan penjelasan ... Juga karena itu akan berubah.

Harap Catatan: pada saat posting ini, file berantakan - ini akan berubah. Tetapi, jika Anda menjalankannya dalam Sublime Text dan mengkompilasinya, atau menjalankannya dari Python, itu akan mengkompilasi dan mengeluarkan banyak informasi - bagian AccessorDB tidak dilakukan (yang akan digunakan untuk memperbarui Print Getters dan GetKeyOutput helper fungsi bersamaan dengan diubah menjadi fungsi Instance, mungkin dimasukkan ke dalam fungsi tunggal dan diganti namanya - lihat ..)

Berikutnya: Tidak semua yang diperlukan untuk menjalankannya - banyak hal yang dikomentari di bagian bawah adalah untuk informasi lebih lanjut yang digunakan untuk debugging - mungkin tidak ada di sana ketika Anda mengunduhnya. Jika ya, Anda harus dapat menghapus komentar dan mengkompilasi ulang untuk mendapatkan informasi lebih lanjut.

Saya mencari solusi untuk memerlukan MyClassBase: pass, MyClass (MyClassBase): ... - jika Anda tahu solusinya - posting.

Satu-satunya hal yang diperlukan di kelas adalah baris __ - str adalah untuk debugging seperti halnya init - mereka dapat dihapus dari Kelas Demo tetapi Anda perlu mengomentari atau menghapus beberapa baris di bawah ini (_foo / 2/3 ) ..

Kelas String, Dict, dan Util di bagian atas adalah bagian dari perpustakaan Python saya - mereka tidak lengkap. Saya menyalin beberapa hal yang saya butuhkan dari perpustakaan, dan saya membuat beberapa hal baru. Kode lengkap akan ditautkan ke perpustakaan lengkap dan akan memasukkannya bersama dengan memberikan panggilan yang diperbarui dan menghapus kode (sebenarnya, satu-satunya kode yang tersisa adalah Kelas Demo dan pernyataan cetak - sistem AccessorFunc akan dipindahkan ke perpustakaan). ..

Bagian dari file:

##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
    pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
    __Name      = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name',      default = 'AccessorFuncDemoClass',  allowed_types = ( TYPE_STRING ),                    allowed_values = VALUE_ANY,                 documentation = 'Name Docs',        getter_prefix = 'Get',  key = 'Name',       allow_erroneous_default = False,    options = { } )
    __x         = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X',         default = 1111,                     allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ),       allowed_values = VALUE_ANY,                 documentation = 'X Docs',           getter_prefix = 'Get',  key = 'x',          allow_erroneous_default = False,    options = { } )
    __Height    = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height',    default = 0,                        allowed_types = TYPE_INTEGER,                       allowed_values = VALUE_SINGLE_DIGITS,       documentation = 'Height Docs',      getter_prefix = 'Get',  key = 'Height',     allow_erroneous_default = False,    options = { } )

Keindahan ini membuatnya sangat mudah untuk membuat kelas baru dengan properti yang ditambahkan secara dinamis dengan AccessorFuncs / callbacks / tipe data / penegakan nilai, dll.

Untuk saat ini, tautannya ada di (Tautan ini harus mencerminkan perubahan pada dokumen.): Https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

Juga: Jika Anda tidak menggunakan Teks Sublime, saya merekomendasikannya pada Notepad ++, Atom, Visual Code, dan lainnya karena implementasi threading yang tepat membuatnya jauh, lebih cepat untuk digunakan ... Saya juga bekerja pada kode seperti IDE sistem pemetaan untuk itu - lihat di: https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (Tambahkan Repo di Package Manager terlebih dahulu, lalu Instal Plugin - ketika versi 1.0.0 siap, saya akan menambahkan ke daftar plugin utama ...)

Saya harap solusi ini membantu ... dan, seperti biasa:

Hanya karena berfungsi, tidak membuatnya benar - Josh 'Acecool' Moser

Acecool
sumber
Saya ingin menambahkan tampilan cepat dari apa yang tampak seperti kelas sehingga Anda tidak perlu membuka file kode tetapi komentar tampaknya tidak mendukungnya ..
Acecool
Rupanya ini mendapat banyak kebencian, yang membingungkan. Itu tidak persis apa yang diminta OP - secara dinamis menambahkan properti ke objek. Itu juga menambahkan fungsi pembantu, yang tidak harus dimasukkan - mungkin itu sebabnya semakin membenci - dan itu juga memastikan pengembang memiliki cara mudah untuk mengakses properti (.x) yang diproses melalui pengambil, yang nilai mentah disimpan (._x) yang bisa menjadi Tidak Ada ketika .x mengembalikan default atau sesuatu yang lain, dan cara untuk mengakses accessor untuk menggunakan pembantu, mengubah hal-hal, dll. (.__ x) ....
Acecool