【问题标题】:Cannot access member variable using abc module and properties in python无法使用 python 中的 abc 模块和属性访问成员变量
【发布时间】:2012-07-07 20:37:13
【问题描述】:

我写了一段代码,模拟abc模块和properties的使用。但是,我似乎无法访问widthheight 变量。代码如下:

from abc import ABCMeta, abstractmethod

class Polygon:

__metaclass__ = ABCMeta

@abstractmethod
def compute_area(self): pass


def __init__(self):
    self.width = None
    self.height = None

@property
def width_prop(self):
    return self.width

@property
def height_prop(self):
    return self.height

@width_setter.setter
def width_setter(self, width):
    self.width = width

@height_setter.setter
def height_setter(self, height):
    self.height = height



class Triangle(Polygon):
    def compute_area(self):
        return 0.5 * width * height




if __name__ == "__main__":
    tri = Triangle()
    tri.height_setter(20)
    tri.width_setter(30)
    print "Area of the triangle = ", tri.compute_area()

我得到的错误信息是NameError: name 'width_setter' is not defined。我的实现可能有什么问题?

编辑:

from abc import ABCMeta, abstractmethod

class Polygon:

__metaclass__ = ABCMeta

@abstractmethod
def compute_area(self): pass


def __init__(self):
    self.width = None
    self.height = None

@property
def width_prop(self):
    return self.width

@width_prop.setter
def width_setter(self, width):
    self.width = width

@property
def height_prop(self):
    return self.height

@height_prop.setter
def height_setter(self, height):
    self.height = height



class Triangle(Polygon):
    def compute_area(self):
        return 0.5 * self.width * self.height




if __name__ == "__main__":
    tri = Triangle()
    tri.height_prop = 20
    tri.width_prop = 30
    print "Area of the triangle = ", tri.compute_area()

【问题讨论】:

  • 你的意思一定是@width_prop.setter@height_prop.setter
  • 是的,我做到了。但还是在抱怨..
  • 您的 setter 函数需要与属性命名相同。另外,请使用更新的代码编辑问题。
  • @Shah:不要让你的 Python 承受所有这些噪音。省略所有的 getter 和 setter,直接访问属性。如果你认为不应该,你需要了解更多关于 Python 的知识。
  • 如果您的 getter 和 setter 只是您在此处显示的内容,那么您就是在浪费时间。您可以简单地直接访问属性并获得完全相同的效果。您链接到的问题是关于是否写get_my_attr@property def my_attr。如果您需要在其中一个或另一个中有代码,那么当然,使用属性。但是如果你没有代码,只是转发一个私有属性,那么跳过整个事情,只使用这个属性。

标签: python properties abc


【解决方案1】:

将 Python 编写为 Python,而不是 C++ 或 Java:

class Polygon:
    def compute_area(self):             # if you need this at all...
        raise NotImplementedError()     # what does it do for you?

    def __init__(self):
        self.width = None
        self.height = None

class Triangle(Polygon):
    def compute_area(self):
        return 0.5 * self.width * self.height

if __name__ == "__main__":
    tri = Triangle()
    tri.height = 20
    tri.width = 30
    print "Area of the triangle = ", tri.compute_area()

【讨论】:

  • 他在基类中有compute_area 的原因是因为这样他可以保证所有子类在定义时都提供它的实现。
  • @Voo:除了基类没有有用的定义,他有什么收获?
  • 提供子类的人会在定义时得到错误,而不仅仅是当有人稍后调用compute_area 时(是的,python 是动态的,但我个人总是喜欢早晚得到错误)。这也是在代码本身中记录子类必须提供的方法而不是仅以注释形式提供的好方法。
  • @voo 您关于文档的观点是有效的,但是您在收到错误时是错误的。如果子类没有实现该方法,那么只有在使用该方法时才会报错。唯一的区别是它是 AttributeError 还是 NotImplementedError。
  • @Tony 好吧,但讨论毕竟是关于使用abc 模块的优势。因此,另一个优势是我们比其他方式更早地得到错误(虽然不像我想象的那么早)。
【解决方案2】:

“固定”(但完全非pythonic)代码:

class Polygon(object):
    __metaclass__ = ABCMeta

    @abstractmethod
    def compute_area(self): 
        pass

    def __init__(self):
        self.width = None
        self.height = None

    @property
    def width_prop(self):
        return self.width

    @width_prop.setter
    def width_prop(self, width):
        self.width = width

    @property
    def height_prop(self):
        return self.height

   @height_prop.setter
   def height_prop(self, height):
    self.height = height


class Triangle(Polygon):
    def compute_area(self):
        return 0.5 * self.width_prop * self.height_prop

if __name__ == "__main__":
    tri = Triangle()
    tri.height_prop = 20
    tri.width_prop = 30
    print "Area of the triangle = ", tri.compute_area()

现在是严肃的部分:Python 不是 Java。您不需要需要 getter/setter 来访问普通属性,因为 Python 可以很好地支持计算属性(您未能正确使用的 property 类型是一个非常简单的泛型 - 但非常方便- 实施)。您的 getter 和 setter 没有做任何有用的事情,并且您将获得完全相同的结果(代码更少,性能更好)直接访问属性:

class Polygon(whatever):
    __metaclass__ = ABCMeta

    def __init__(self, witdh=None, height=None):
        self.width = width
        self.height = height


    @abstractmethod
    def compute_area(self): 
        pass


class Triangle(Polygon):
    def compute_area(self):
        return 0.5 * self.width * self.height


if __name__ == "__main__":
    tri = Triangle(20, 30)
    print "Area of the triangle = ", tri.compute_area()

作为记录,Python 没有“private”/“public”关键字 - 只有以下划线开头的名称的约定是“实现的东西”,不应该访问客户端代码。请注意,“不应该”仅表示其含义:没有什么可以阻止您访问它们,但是如果现在或下一个版本出现任何问题,请不要抱怨。一种“如果未密封,则保修无效”。

wrt/ 正确使用或属性:我不会详细介绍(需要深入的 Python 执行模型和对象模型解释),但正确的语法是:

class MyClass(object):

    def __init__(self, prop):
        # this will invoke prop.setter
        self.prop = prop

    # defines a property named "prop"
    # will be accessed just like a plain
    # attribute but will go thru the getter and setter

    @property
    def prop(self):
        # this is the getter
        return self._some_val * 42

    # now add a setter to 'prop':
    @prop.setter
    def prop(self, val):
        self._some_val = val / 42


obj = MyClass(10)
print obj.prop
obj.prop = 5
print obj.prop

另外(最后):Python 没有“隐式 this”(或“隐式 self”)。您必须在方法中使用self.attr 来访问任何属性。

【讨论】:

  • 我实际上想保持使用 abc 作为我上面的代码。我仍然收到此错误:TypeError: 'NoneType' object is not callable
  • 请阅读编辑。 wrt/你的错误:它带有完整的回溯,请阅读它,你应该能够自己修复代码(或者从官方 Python 教程开始 - 你显然应该这样做)
  • 我认为您的“正确语法”行不通;使用 whatever 而不是 prop 应该会生成 AttributeError。
  • 我也试过你的编辑版本,但它仍然抱怨“tri.height_prop = 20 AttributeError:无法设置属性”
  • @Shah :参见 DSM 评论。我想这就是我应该在深夜发帖的原因:/
【解决方案3】:

edit:在这种简单的情况下,例如使用直接变量访问。请参阅 Ned Batchelder 的回答。

如果您在访问变量时需要额外的功能,您可以使用它。


http://docs.python.org/library/functions.html#property

from abc import ABCMeta, abstractmethod

class Polygon(object):

    __metaclass__ = ABCMeta

    @abstractmethod
    def compute_area(self): 
        pass


    def __init__(self):
        self._width = None
        self._height = None

    @property
    def width(self):
        getting_variable_value()
        return self._width

    @width.setter
    def width(self, width):
        setting_variable_value()
        self._width = width

    @property
    def height(self):
        getting_variable_value()
        return self._height

    @height.setter
    def height(self, height):
        setting_variable_value()
        self._height = height



class Triangle(Polygon):
    def compute_area(self):
        return 0.5 * self.width * self.height


if __name__ == "__main__":
    tri = Triangle()
    tri.height = 20
    tri.width = 30
    print "Area of the triangle = ", tri.compute_area()

【讨论】:

  • 这太可怕了。为什么要这样写 getter 和 setter? Python 不是 Java,也不需要所有这些噪音。
  • -1 同意 Ned “可行”是最低要求不是唯一要求。
猜你喜欢
  • 2020-01-26
  • 1970-01-01
  • 2010-12-17
  • 2018-06-04
  • 1970-01-01
  • 1970-01-01
  • 2022-07-07
  • 2011-01-17
  • 2013-11-23
相关资源
最近更新 更多