Как получить ссылку на текущий объект класса? - PullRequest
4 голосов
/ 31 августа 2011

Как в Python получить ссылку на текущий объект класса внутри оператора класса? Пример:

def setup_class_members(cls, prefix):
    setattr(cls, prefix+"_var1", "hello")
    setattr(cls, prefix+"_var2", "goodbye")

class myclass(object):
    setup_class_members(cls, "coffee")  # How to get "cls"?

    def mytest(self):
        print(self.coffee_var1)
        print(self.coffee_var2)

x = myclass()
x.mytest()

>>> hello
>>> goodbye


Альтернативы, которые я списал со счетов:

  1. Использовать locals(): Это дает диктат в классе, который может быть записан. Похоже, это работает для классов, однако документация говорит вам не делать этого. (Я мог бы испытать искушение пойти с этой альтернативой, если кто-то может заверить меня, что это продолжит работать в течение некоторого времени.)

  2. Добавление членов к объекту класса после оператора class. Мое реальное приложение - получить класс PyQt4 QWidget с динамически созданными атрибутами класса pyqtProperty. QWidget необычен тем, что имеет собственный метакласс. Грубо говоря, метакласс составляет список pyqtProperties и сохраняет его в качестве дополнительного члена. По этой причине свойства, добавленные в класс после создания, не действуют. Пример, чтобы прояснить это:

from PyQt4 import QtCore, QtGui


# works
class MyWidget1(QtGui.QWidget):
    myproperty = QtCore.pyqtProperty(int)


# doesn't work because QWidget's metaclass doesn't get to "compile" myproperty
class MyWidget2(QtGui.QWidget):
    pass
MyWidget2.myproperty = QtCore.pyqtProperty(int)

Обратите внимание, что вышеперечисленное будет работать для большинства случаев программирования; мой случай оказался одним из тех необычных угловых случаев.

Ответы [ 4 ]

3 голосов
/ 31 августа 2011

AFAIK есть два способа сделать то, что вы хотите:

  1. Используя метакласс , это создаст ваши две переменные во время создания класса (что, я думаю,что вы хотите):

    class Meta(type):
        def __new__(mcs, name, bases, attr):
            prefix = attr.get("prefix")
            if prefix:
                attr[prefix+"_var1"] = "hello"
                attr[prefix+"_var2"] = "goodbye"
    
            return type.__new__(mcs, name, bases, attr)
    
    class myclass(object):
        __metaclass__ = Meta
        prefix = "coffee"
    
        def mytest(self):
            print(self.coffee_var1)
            print(self.coffee_var2)
    
  2. Создайте две переменные класса во время создания экземпляра:

    class myclass (object): prefix = "coffee"

     def __init__(self):     
         setattr(self.__class__, self.prefix+"_var1", "hello")
         setattr(self.__class__, self.prefix+"_var2", "goodbye")
    
     def mytest(self):
         print(self.coffee_var1)
         print(self.coffee_var2)
    

Примечание: я не уверен, чего вы хотите достичь, потому что, если вы хотите создавать динамические переменные в зависимости от переменной prefix, почему вы обращаетесь так же, как вы делаете в своем mytestметод ?!Я надеюсь, что это был просто пример.

3 голосов
/ 31 августа 2011

Для Python 3 класс должен быть объявлен как

class myclass(object, metaclass = Meta):
   prefix = "coffee"
   ...


Несколько других моментов:

  • Метакласс может быть вызываемым, а не просто классом (Python 2 и 3)

  • Если базовый класс вашего класса уже имеет нестандартный метакласс, вы должны обязательно вызывать его методы __init__() и __new__() вместо типов.

  • Оператор класса принимает параметры ключевых слов, которые передаются метаклассу (только Python 3)

Переписать решение Муада в Python 3, используя все вышеперечисленное:

def MetaFun(name, bases, attr, prefix=None):
    if prefix:
        attr[prefix+"_var1"] = "hello"
        attr[prefix+"_var2"] = "goodbye"

    return object.__class__(name, bases, attr)

class myclass(object, metaclass = MetaFun, prefix="coffee"):
    def mytest(self):
        print(self.coffee_var1)
        print(self.coffee_var2)
2 голосов
/ 31 августа 2011

Вы можете использовать еще два подхода:

Декоратор класса.

def setup_class_members(prefix):

    def decorator(cls):
        setattr(cls, prefix+"_var1", "hello")
        setattr(cls, prefix+"_var2", "goodbye")
        return cls

    return decorator

@setup_class_members("coffee")
class myclass(object):
    # ... etc

Особенно, если вам нужно добавить атрибуты в различных комбинациях, подход декоратора хорош, потому что он не имеетлюбой эффект на наследование.

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

class coffee_mixin(object):
    coffee_var1 = "hello"
    coffee_var2 = "goodbye"

class tea_mixin(object):
    tea_var1 = "good morning old bean"
    tea_var2 = "pip pip cheerio"

class myclass(coffee_mixin, tea_mixin):
    # ... etc
1 голос
/ 31 августа 2011

См. zope.interface.declarations._implements, где приведен пример создания такого рода магии. Просто имейте в виду, что это серьезный риск для обслуживания и переносимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...