У меня есть класс, который содержит объект-член. Я хотел бы вызывать методы члена, как если бы они были методами класса контейнера.
Следующий пример иллюстрирует (но в другом контексте), что я хотел бы сделать.
Предположим, у нас есть два класса, представляющих два разных вида товаров: Игрушки и Обувь .
Каждый класс имеет два метода, скажем, weight()
и description()
, которые явно возвращают вес элемента и описание элемента.
Теперь я хочу разместить эти продукты в отдельных коробках - каждая коробка будет содержать либо один Toy
, либо один Shoe
.
Я хочу иметь возможность доступа к методам weight()
и description()
, однако я хочу, чтобы они были членами экземпляра Box
. Кроме того, я не хочу специально реализовывать методы weight()
и description()
в классе Box
, потому что я не хочу реализовывать больше методов в классе Box
всякий раз, когда добавляется новый метод в игрушку и обувь.
Вот пример «конечного результата», который сделал бы меня счастливым:
# Define our products
product1 = Toy('plastic gun', 200)
product2 = Shoe('black leather shoes', 500)
# Now place them in boxes
box1 = Box(product1, box_code='A1234')
box2 = Box(product2, box_code='V4321')
# I want to know the contents of the boxes
box1.description()
box2.description()
# Desired results
>> plastic gun
>> black leather shoes
Моя идея реализации заключается в следующем.
class Product():
def __init__(self, description, weight):
self.desc = description
self.wght = weight
def description(self):
return self.desc
def weight(self):
return self.weight
class Toy(Product):
pass
class Shoe(Product):
pass
class Box(object):
def __init__(self, product, box_code):
self.box_code = box_code
self.product = product
def __getattribute__(self, name):
# Any method that is accessed in the Box instance should be replaced by
# the correspondent method in self.product
return self.product.__getattribute__(name)
Однако это не работает! Если я запускаю «нужный» пример, показанный в первом блоке кода, я получаю бесконечную рекурсию. Итак, как элегантный способ сделать это?
Обратите внимание, что Box
имеет свой собственный параметр box_code
и что в будущем я мог бы захотеть добавить новые методы к Toy
и Shoes
(например, промокод или что-то еще), не имея изменить класс Box
.
Хорошо, ребята, я с нетерпением жду ваших ответов.
Обновление : ответ на проблему
Спасибо Interjay и Майку Буру за помощь.
Мне просто нужно было заменить определение метода __getattribute__
в классе Box
на __getattr__
, и оно отлично работало, как это было предложено Interjay. Исправленное определение класса Box:
class Box(object):
def __init__(self, product, box_code):
self.box_code = box_code
self.product = product
def __getattr__(self, name):
# Any method that is accessed in the Box instance should be replaced by
# the correspondent method in self.product
return self.product.__getattribute__(name)
Обновление : Завершение примера с использованием __setattr__
После того, как я исправил проблему с помощью метода getattr, я заметил, что у моих классов не было желаемого поведения, когда я хотел установить атрибут. Например, если я попытался:
box1.desc = 'plastic sword'
вместо изменения описания product1 (которое было инкапсулировано в box1), в box1 был создан новый элемент с именем "desc". Чтобы решить эту проблему, мне нужно было определить функцию __setattr__
, как показано ниже:
def __setattr__(self, name, value):
setattr(self.product, name, value)
Однако простое добавление этой функции создает рекурсивный вызов, потому что в __init__
я пытаюсь назначить self.product = product
', который вызывает функцию __setattr__
, которая не находит члена self.product
, поэтому вызывает функцию __setattr__
снова, рекурсивно до бесконечности.
Чтобы избежать этой проблемы, я должен изменить метод __init__
, чтобы назначить нового члена, используя словарь класса. Полное определение класса Box
таково:
class Box(object):
def __init__(self, product, box_code):
self.__dict__['product'] = product
self.box_code = box_code
def __getattr__(self, name):
# Any method that is accessed in the Box instance should be replaced by
# the correspondent method in self.product
return getattr(self.product, name)
def __setattr__(self, name, value):
setattr(self.product, name, value)
Теперь забавная вещь ... Если я сначала определю элемент self.product
, как показано выше, программа работает нормально. Однако, если я попытаюсь сначала определить элемент self.box_code
, программа снова столкнется с проблемой рекурсивного цикла. Кто-нибудь знает почему?