личность объекта python - PullRequest
       3

личность объекта python

0 голосов
/ 10 января 2020

Часто я оказываюсь в ситуации, когда я хочу внедрить своего рода «идентичность» в экземпляр пользовательского класса, и в зависимости от вида экземпляр должен демонстрировать различное поведение.

class Foo:
    def __init__(self, identity):
       self.identity = identity

    def bar(self):
       if self.identity = 'A':
           do_something
       elif self.identity = 'B':
           sth_else

Так что сейчас я использую сравнение строк для этой задачи, но, исходя из C ++ и его пресловутого стремления к безопасности типов, это кажется мне странным. Какой самый питон c способ достижения того, чего я хочу?

Ответы [ 2 ]

1 голос
/ 10 января 2020

Использовать наследование:

class Foo:
    ...

class Foo_A(Foo):
    def bar(self):
       do_something

class Foo_B(Foo):
    def bar(self):
       sth_else

Затем просто создайте экземпляр Foo_A или Foo_B в зависимости от ситуации.

Если вы хотите изменить «идентичность» объекта во время его тогда жизненный цикл identity будет неправильным именем для него.

Обычно, если вам нужен объект с поведением 'A', вы создаете Foo_A, а затем, когда вам нужен объект с поведением 'B', вы выбросить исходный объект и создать экземпляр Foo_B. Сделайте объекты неизменяемыми, если это вообще возможно, вы будете благодарны позже, если сделаете это сейчас.

Как предлагается в комментариях, вы можете изменить тип класса, чтобы изменить поведение переопределенных методов. Это работает в Python и для некоторых проблем является самым чистым решением, но это редко. Обычно лучше объединять переменные объекты (но лучше просто не изменять исходные объекты). Вот возможное решение с использованием агрегации:

class Foo:
    def __init__(self, identity):
        self.identity = identity

    @property
    def identity(self):
        return self._processor.identity

    @identity.setter
    def identity(self, identity):
        self._processor = FooProcessor(identity, self)

    def bar(self):
        self._processor.bar()

class FooProcessor:
    def __new__(cls, identity, foo):
        for subclass in cls.__subclasses__():
            if identity == subclass.identity:
                return object.__new__(subclass)
        raise RuntimeError(f'unknown identity {identity}')

    def __init__(self, identity, foo):
        assert identity == self.identity
        self.foo = foo

class Foo_A(FooProcessor):
    identity = 'A'
    def bar(self):
        print(f'do_something with {self.foo}')

class Foo_B(FooProcessor):
    identity = 'B'
    def bar(self):
        print(f'something else with {self.foo}')


foo = Foo('A')
foo.bar()
foo.identity = 'B'
foo.bar()
assert foo.identity == 'B'

А вот версия, которая изменяет __class__:

class Foo:
    def __init__(self, identity):
        self.identity = identity

    @property
    def identity(self):
        return self._identity

    @identity.setter
    def identity(self, identity):
        for subclass in Foo.__subclasses__():
            if identity == subclass._identity:
                self.__class__ = subclass
                return
        raise RuntimeError(f'unknown identity {identity}')

class Foo_A(Foo):
    _identity = 'A'
    def bar(self):
        print(f'do_something with {self} {self.identity}')

class Foo_B(Foo):
    _identity = 'B'
    def bar(self):
        print(f'something else with {self} {self.identity}')


foo = Foo('A')
foo.bar()
foo.identity = 'B'
foo.bar()
assert foo.identity == 'B'

Вывод выглядит примерно так:

do_something with <__main__.Foo_A object at 0x10c022af0> A
something else with <__main__.Foo_B object at 0x10c022af0> B
0 голосов
/ 10 января 2020

С

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

, идя против наследования, возможно, вы могли бы разделить это динамическое c поведение в классы поведения, такие как

class BaseFooBehavior:
    @classmethod
    def bar(cls, foo):
        raise NotImplementedError('...')


class FooBehavior1(BaseFooBehavior):
    @classmethod
    def bar(cls, foo):
        return foo.a * foo.b


class FooBehavior2(BaseFooBehavior):
    @classmethod
    def bar(cls, foo):
        return str(foo.a) * foo.b


class Foo:
    behavior_cls = FooBehavior1  # default behavior
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def bar(self):
        return self.behavior_cls.bar(self)


foo = Foo(a=42, b=12)
print(foo.bar())
foo.behavior_cls = FooBehavior2  # change the instance's behavior
print(foo.bar())

распечатка

504
424242424242424242424242
...