Переклассификация экземпляра в Python

Попробуйте это:

Чтобы отогнуть изображение, используйте для изображения вложенный div и присвойте ему противоположное значение перекоса. Таким образом, если у вас есть 20deg для родительского элемента, то вы можете присвоить вложенному (image) div значение перекоса -20deg.

.container {
  overflow: hidden;
}

#parallelogram {
  width: 150px;
  height: 100px;
  margin: 0 0 0 -20px;
  -webkit-transform: skew(20deg);
  -moz-transform: skew(20deg);
  -o-transform: skew(20deg);
  background: red;
  overflow: hidden;
  position: relative;
}

.image {
  background: url(http://placekitten.com/301/301);
  position: absolute;
  top: -30px;
  left: -30px;
  right: -30px;
  bottom: -30px;
  -webkit-transform: skew(-20deg);
  -moz-transform: skew(-20deg);
  -o-transform: skew(-20deg);
}

Пример:

http://jsfiddle.net/diegoh/mXLgF / 1154 /

56
задан balpha 15 June 2009 в 15:41
поделиться

7 ответов

Reclassing instances like this is done in Mercurial (a distributed revision control system) when extensions (plugins) want to change the object that represent the local repository. The object is called repo and is initially a localrepo instance. It is passed to each extension in turn and, when needed, extensions will define a new class which is a subclass of repo.__class__ and change the class of repo to this new subclass!

It looks like this in code:

def reposetup(ui, repo):
    # ...

    class bookmark_repo(repo.__class__): 
        def rollback(self):
            if os.path.exists(self.join('undo.bookmarks')):
                util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))
            return super(bookmark_repo, self).rollback() 

        # ...

    repo.__class__ = bookmark_repo 

The extension (I took the code from the bookmarks extension) defines a module level function called reposetup. Mercurial will call this when initializing the extension and pass a ui (user interface) and repo (repository) argument.

The function then defines a subclass of whatever class repo happens to be. It would not suffice to simply subclass localrepo since extensions need to be able to extend each other. So if the first extension changes repo.__class__ to foo_repo, the next extension should change repo.__class__ to a subclass of foo_repo and not just a subclass of localrepo. Finally the function changes the instanceø's class, just like you did in your code.

I hope this code can show a legitimate use of this language feature. I think it's the only place where I've seen it used in the wild.

19
ответ дан 26 November 2019 в 17:32
поделиться

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

3
ответ дан 26 November 2019 в 17:32
поделиться

Я не уверен, что в данном случае лучше всего использовать наследование (по крайней мере, в отношении в «переклассификацию»). Похоже, вы на правильном пути, но похоже, что для этого лучше всего подойдет композиция или агрегирование. Вот пример того, о чем я думаю (в непроверенном псевдо-коде):

from copy import copy

# As long as none of these attributes are defined in the base class,
# this should be safe
class SkilledProgrammer(Programmer):
    def __init__(self, *skillsets):
        super(SkilledProgrammer, self).__init__()
        self.skillsets = set(skillsets)

def teach(programmer, other_programmer):
    """If other_programmer has skillsets, append this programmer's
       skillsets.  Otherwise, create a new skillset that is a copy
       of this programmer's"""
    if hasattr(other_programmer, skillsets) and other_programmer.skillsets:
        other_programmer.skillsets.union(programmer.skillsets)
    else:
        other_programmer.skillsets = copy(programmer.skillsets)
def has_skill(programmer, skill):
    for skillset in programmer.skillsets:
        if skill in skillset.skills
            return True
    return False
def has_skillset(programmer, skillset):
    return skillset in programmer.skillsets


class SkillSet(object):
    def __init__(self, *skills):
        self.skills = set(skills)

C = SkillSet("malloc","free","pointer arithmetic","curly braces")
SQL = SkillSet("SELECT", "INSERT", "DELETE", "UPDATE")

Bob = SkilledProgrammer(C)
Jill = Programmer()

teach(Bob, Jill)          #teaches Jill C
has_skill(Jill, "malloc") #should return True
has_skillset(Jill, SQL)   #should return False

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

но похоже, что для этого лучше всего подойдет композиция или агрегирование. Вот пример того, о чем я думаю (в непроверенном псевдо-коде):

from copy import copy

# As long as none of these attributes are defined in the base class,
# this should be safe
class SkilledProgrammer(Programmer):
    def __init__(self, *skillsets):
        super(SkilledProgrammer, self).__init__()
        self.skillsets = set(skillsets)

def teach(programmer, other_programmer):
    """If other_programmer has skillsets, append this programmer's
       skillsets.  Otherwise, create a new skillset that is a copy
       of this programmer's"""
    if hasattr(other_programmer, skillsets) and other_programmer.skillsets:
        other_programmer.skillsets.union(programmer.skillsets)
    else:
        other_programmer.skillsets = copy(programmer.skillsets)
def has_skill(programmer, skill):
    for skillset in programmer.skillsets:
        if skill in skillset.skills
            return True
    return False
def has_skillset(programmer, skillset):
    return skillset in programmer.skillsets


class SkillSet(object):
    def __init__(self, *skills):
        self.skills = set(skills)

C = SkillSet("malloc","free","pointer arithmetic","curly braces")
SQL = SkillSet("SELECT", "INSERT", "DELETE", "UPDATE")

Bob = SkilledProgrammer(C)
Jill = Programmer()

teach(Bob, Jill)          #teaches Jill C
has_skill(Jill, "malloc") #should return True
has_skillset(Jill, SQL)   #should return False

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

но похоже, что для этого лучше всего подойдет композиция или агрегирование. Вот пример того, о чем я думаю (в непроверенном псевдо-коде):

from copy import copy

# As long as none of these attributes are defined in the base class,
# this should be safe
class SkilledProgrammer(Programmer):
    def __init__(self, *skillsets):
        super(SkilledProgrammer, self).__init__()
        self.skillsets = set(skillsets)

def teach(programmer, other_programmer):
    """If other_programmer has skillsets, append this programmer's
       skillsets.  Otherwise, create a new skillset that is a copy
       of this programmer's"""
    if hasattr(other_programmer, skillsets) and other_programmer.skillsets:
        other_programmer.skillsets.union(programmer.skillsets)
    else:
        other_programmer.skillsets = copy(programmer.skillsets)
def has_skill(programmer, skill):
    for skillset in programmer.skillsets:
        if skill in skillset.skills
            return True
    return False
def has_skillset(programmer, skillset):
    return skillset in programmer.skillsets


class SkillSet(object):
    def __init__(self, *skills):
        self.skills = set(skills)

C = SkillSet("malloc","free","pointer arithmetic","curly braces")
SQL = SkillSet("SELECT", "INSERT", "DELETE", "UPDATE")

Bob = SkilledProgrammer(C)
Jill = Programmer()

teach(Bob, Jill)          #teaches Jill C
has_skill(Jill, "malloc") #should return True
has_skillset(Jill, SQL)   #should return False

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

11
ответ дан 26 November 2019 в 17:32
поделиться

Хе-хе, забавный пример.

«Переклассификация» на первый взгляд довольно странная. А как насчет подхода «конструктор копирования»? Вы можете сделать это с помощью Reflection-like hasattr , getattr и setattr . Этот код скопирует все из одного объекта в другой, если он еще не существует. Если вы не хотите копировать методы, вы можете их исключить; см. прокомментированный if .

class Foo(object):
    def __init__(self):
        self.cow = 2
        self.moose = 6

class Bar(object):
    def __init__(self):
        self.cat = 2
        self.cow = 11

    def from_foo(foo):
        bar = Bar()
        attributes = dir(foo)
        for attr in attributes:
            if (hasattr(bar, attr)):
                break
            value = getattr(foo, attr)
            # if hasattr(value, '__call__'):
            #     break # skip callables (i.e. functions)
            setattr(bar, attr, value)

        return bar

Все это отражение некрасиво, но иногда вам нужна уродливая машина отражения, чтобы делать крутые вещи. ;)

1
ответ дан 26 November 2019 в 17:32
поделиться

Мне эта техника кажется вполне питонической. Композиция также была бы хорошим выбором, но присвоение __ class __ вполне допустимо (см. здесь для рецепта, который использует его немного по-другому).

1
ответ дан 26 November 2019 в 17:32
поделиться

Я скажу, что это прекрасно, если это сработает для вас.

-2
ответ дан 26 November 2019 в 17:32
поделиться

«Шаблон состояния позволяет объекту изменять свое поведение при изменении его внутреннего состояния. Создается впечатление, что объект меняет свой класс». - Шаблон дизайна «Сначала голова». Что-то очень похожее пишут Gamma et.al. в их книге "Паттерны дизайна". (У меня он есть в другом месте, поэтому без цитаты). Я думаю, что в этом весь смысл этого паттерна проектирования. Но если я могу изменить класс объекта во время выполнения, большую часть времени мне не нужен шаблон (бывают случаи, когда State Pattern не просто имитирует изменение класса).

Кроме того, изменение класса во время выполнения не всегда работает:

class A(object):
    def __init__(self, val):
        self.val = val
    def get_val(self):
        return self.val

class B(A):
    def __init__(self, val1, val2):
        A.__init__(self, val1)
        self.val2 = val2
    def get_val(self):
        return self.val + self.val2


a = A(3)
b = B(4, 6)

print a.get_val()
print b.get_val()

a.__class__ = B

print a.get_val() # oops!

Кроме того, я рассматриваю возможность изменения класса во время выполнения Pythonic и время от времени использую его.

3
ответ дан 26 November 2019 в 17:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: