#python #properties #language-features
#python #свойства #язык-особенности
Вопрос:
Рассмотрим следующий пример:
class A:
@property
def x(self): return 5
Итак, конечно, вызов a = A(); a.x
вернет 5
Но представьте, что вы хотите иметь возможность изменять свойство x.
Таким образом, например:
class A:
@property
def x(self, neg = False): return 5 if not neg else -5
И вызвать его с a = A(); a.x(neg=True)
Это вызовет ошибку типа: 'int' object is not callable
, что вполне нормально, поскольку our x
оценивается как 5
.
Итак, я хотел бы знать, как можно передать более одного аргумента получателю свойств, если это вообще возможно.
Ответ №1:
Обратите внимание, что вам не обязательно использовать property
в качестве декоратора. Вы вполне можете использовать его старым способом и предоставлять отдельные методы в дополнение к свойству:
class A:
def get_x(self, neg=False):
return -5 if neg else 5
x = property(get_x)
>>> a = A()
>>> a.x
5
>>> a.get_x()
5
>>> a.get_x(True)
-5
Это может быть или не быть хорошей идеей, в зависимости от того, что именно вы с ней делаете (но я ожидал бы увидеть отличное обоснование в комментарии, если бы я наткнулся на этот шаблон в любом коде, который я просматривал)
Комментарии:
1. Чувак … это на самом деле блестящая идея. Хотелось бы, чтобы другие API использовали это вместо только установщиков-getters / only properties.
2. Больше декораторов? Все они поддерживают использование таким образом, поскольку синтаксис декоратора — это просто сахар для вызова функции и повторной привязки имени.
3. О, я понимаю, что вы имеете в виду — больше API, предоставляющих как методы установки / получения, так и связанные с ними свойства.
4. Идея инкапсуляции здесь отсутствует. Нет смысла использовать геттеры таким образом. Там нарушен интерфейс.
5. Я бы использовал вместо
x = property(get_x)
,@property def x(self): return self.get_x()
. Более читаемый.
Ответ №2:
Я думаю, вы не до конца поняли назначение свойств.
Если вы создадите свойство x
, вы получите к нему доступ с помощью obj.x
вместо obj.x()
. После создания свойства нелегко вызвать базовую функцию напрямую.
Если вы хотите передать аргументы, назовите свой метод get_x
и не делайте его свойством:
def get_x(self, neg=False):
return 5 if not neg else -5
Если вы хотите создать средство установки, сделайте это следующим образом:
class A:
@property
def x(self): return 5
@x.setter
def x(self, value): self._x = value
Комментарии:
1. Я знаю! И вы можете видеть
a.x
вызов в моем первом примере.2. @Rizo: свойства просто не предназначены для этого.
Ответ №3:
свойство должно зависеть только от связанного объекта. Если вы хотите использовать некоторые внешние параметры, вам следует использовать методы.
Комментарии:
1. Предположим, вы используете want для доступа к свойству после некоторой проверки пароля, как это будет без передачи этого пароля получателю.
2. @pentanol Я бы сказал, что метод свойств — это не место для кода аутентификации API.
Ответ №4:
В вашем втором примере вы используете a.x()
так, как если бы это была функция: a.x(neg=True)
. Имея это в виду, почему бы просто не определить это как функцию?
Комментарии:
1. Это прямое решение, но я использую очень сложные средства получения и установки и хотел бы продолжать использовать их без необходимости писать отдельные функции для атрибутов.
2. Это не отвечает на мой вопрос. Теперь Я могу использовать функции на python! 🙂 Я просто хочу знать, есть ли какой-нибудь способ заставить свойство действовать как функция.
3. @Rizo почему? Если вы хотите, чтобы это действовало как функция, тогда используйте функцию (метод). Но в ответ на ваш вопрос, единственным способом было бы вернуть вызываемый объект из свойства, которое принимает параметр. Тогда у вас было бы что-то, что выглядит точно так же, как функция, но менее эффективно.
4. Если средство получения делает что-либо, кроме возврата простого значения (или значения по умолчанию), это больше не «средство получения», а обычная функция.
5. @Rizo: в примере, который вы приводите, вероятно, разумнее просто позволить вызывающему пользователю решать, что делать с возвращаемым значением. в конце концов, это вызывающий объект, передающий условие.
Ответ №5:
Я знаю, что этот вопрос устарел, но для справки вы можете вызвать свое свойство с таким аргументом:
a = A()
assert a.x == 5
assert A.x.fget(a, True) == -5
Как упоминали другие, это не рекомендуется.
Ответ №6:
В этом конкретном случае вы могли бы определить два свойства, которые вызывают базовую функцию:
class A:
@property
def x(self):
return self._x(neg = False)
@property
def x_neg(self):
return self._x(neg = True)
def _x(self, neg):
return 5 if not neg else -5
Ответ №7:
Я только что столкнулся с этой проблемой. У меня есть класс Polynomial(), и я определяю градиент, который я хотел бы возвращать функцией, если нет аргументов, или оценивать, если есть аргументы. Я хочу сохранить градиент как атрибут, чтобы мне не нужно было вычислять его каждый раз, когда мне нужно его использовать, и я хочу использовать @property, чтобы атрибут gradient вычислялся лениво. Мое решение состояло в том, чтобы определить класс Gradient с определенным методом вызова для возвращаемого свойства grad полинома.
@property
def grad(self):
"""
returns gradient vector
"""
class Gradient(list):
def __call__(self, *args, **kwargs):
res = []
for partial_derivative in g:
res.append(partial_derivative(*args, **kwargs))
return res
g = Gradient()
for i in range(1, len(self.term_matrix[0])):
g.append(self.derivative(self.term_matrix[0][i]))
return g
И тогда у меня следующие тесты успешно проходят:
def test_gradient(self):
f = Polynomial('x^2y y^3 xy^3')
self.assertEqual(f.grad, [Polynomial('2xy y^3'), Polynomial('x^2 3xy^2 3y^2')])
self.assertEqual(f.grad(x=1, y=2), [12, 25])
f = Polynomial('x^2')
self.assertEqual(f.grad(1), [2])
Итак, для решения этой проблемы мы могли бы попробовать:
class A:
@property
def x(self):
class ReturnClass(int):
def __call__(self, neg=False):
if not neg:
return 5
return -5
return ReturnClass()
Комментарии:
1. Не уверен, что решение работает: если я скажу,
a = A(); print(a.x)
я получу 0 в результате — значение по умолчанию для неинициализированного int. Решение — насколько я понимаю — должно быть5
дляa.x
.