Динамическое создание свойств класса - PullRequest
0 голосов
/ 30 ноября 2018

Взгляните на этот фрагмент кода:

class Face():
    pass

class Cube():
    def __init__(self):
        self.faces = {
                'front': Face(1),
                ...
                }

    @property
    def front(self):
        return self.faces['front']

    @front.setter
    def front(self, f):
        pass

Я создал геттеры и сеттеры для всех граней.Есть ли способ сделать этот код более компактным, возможно, путем динамического создания методов получения и установки?

Ответы [ 4 ]

0 голосов
/ 30 ноября 2018

Ваш код является избыточным, поскольку экземпляры атрибутов уже хранятся в словаре, который является свойством __ dict __ .Я признаю, что вы сосредоточены на написании своего кода в несколько строк.Это хорошая задача, чтобы продолжать расти, но в долгосрочной перспективе вы должны сосредоточиться на ясности своего кода.

Вот более простой способ написания кода без использования свойств:

class Face():
    pass

class Cube():
    def __init__(self):
        self.front = Face()
        self.rear = Face()

Принцип инкапсуляции заключается в том, что вы должны скрывать свои «атрибуты» за «свойствами».Несмотря на то, что это не строго соблюдается в python, это неплохая идея.Вот правильный способ сделать это:

class Face():
    pass

class Cube():
    def __init__(self):
        self._front = Face()

    @property
    def front(self):
        return self._front

    @front.setter
    def front(self, value):
        self._front = value

Чтобы ответить на ваш вопрос в конце, да, вы можете динамически создавать свойства.https://stackoverflow.com/a/1355444/3368572

Но имейте в виду, что написание динамического кода должно быть зарезервировано для особых случаев, поскольку вашей среде IDE будет сложнее следить за ходом вашей программы.Если вы используете соглашения так, как они предназначены, тогда ваш код станет понятен людям и вашей IDE.

0 голосов
/ 30 ноября 2018

, если вы действительно хотите это сделать, вы можете использовать __getattr__ и __setattr__:

class Cube:

   ...   

   def __getattr__(self, item):
        return self.faces[item]

   def __setattr__(self, item, value):
        self.faces[item] = value

, но при установке front в__init__ metaoud, вы также можете сделать его постоянным членом ...

0 голосов
/ 30 ноября 2018

В следующем коде предполагается, что у вас

  • есть причина иметь диктовку self.faces вместо установки таких атрибутов, как front, непосредственно в экземпляре

  • и / или хотите реализовать какую-либо значимую логику получения и установки для ключей в self.faces.

В противном случае, это упражнение довольно бессмысленно, поскольку, как заметил вас Corentin Limierможно просто установить self.front = Face(1) и т. д.


Вы можете использовать дескрипторы, переменную класса, содержащую имена лиц, и декоратор класса.Думайте о дескрипторах как о свойствах многократного использования.

В следующем примере кода я добавил num переменную экземпляра к Face и грань 'side' только для демонстрационных целей.

class FaceDescriptor:
    def __get__(self, instance, owner):
        # your custom getter logic

        # dummy implementation
        if instance is not None:                
            return instance.faces[self.face]

    def __set__(self, instance, value):
        # your custom setter logic

        # dummy implementation
        instance.faces[self.face] = value

def set_faces(cls):
     for face in cls._faces:
         desc = FaceDescriptor()
         desc.face = face
         setattr(cls, face, desc)
     return cls

class Face():
    def __init__(self, num):
        self.num = num   

@set_faces
class Cube():
    _faces = ['front', 'side']

    def __init__(self):
        self.faces = {face:Face(i) for i, face in enumerate(self._faces, 1)}

В действии:

>>> c = Cube()                                                                                                                                                                                         
>>> c.front.num                                                                                                                                                                                         
1
>>> c.side.num                                                                                                                                                                                          
2
>>> c.front = 'stuff'                                                                                                                                                                                   
>>> c.front                                                                                                                                                                                             
'stuff'
>>> c.faces                                                                                                                                                                                             
{'front': 'stuff', 'side': <__main__.Face at 0x7fd0978f37f0>}
0 голосов
/ 30 ноября 2018

Предполагая, что это все, что делает ваш класс, вы можете сделать что-то вроде

class Cube:
   ...

def __getattr__(self, name):
    return self.faces[name]

def __setattr__(self, name, value):
    self.faces[name] = value
...