【问题标题】:Why does "self" outside a function's parameters give a "not defined" error?为什么函数参数之外的“self”会给出“未定义”错误?
【发布时间】:2012-04-03 04:14:25
【问题描述】:

看看这段代码:

class MyClass():

    # Why does this give me "NameError: name 'self' is not defined":
    mySelf = self

    # But this does not?
    def myFunction(self):
        mySelf2 = self

基本上我想要一个类引用自身而不需要具体命名自己的方法,因此我希望 self 为该类工作,而不仅仅是方法/函数。我怎样才能做到这一点?

编辑: 重点是我试图从类本身内部引用类名,例如 self.class._name_ 这样类名就不会在类代码的任何地方硬编码,因此更容易重用代码。

编辑 2:根据我从以下答案中了解到的情况,我想做的事情是不可能的。我得另辟蹊径。任务放弃。

编辑 3:以下是我正在尝试做的具体操作:

class simpleObject(object):
    def __init__(self, request):
        self.request = request

@view_defaults(renderer='string')
class Test(simpleObject):

    # this line throws an error because of self
    myClassName = self.__class__.__name__

    @view_config(route_name=myClassName)
    def activateTheView(self):
        db = self.request.db
        foo = 'bar'

        return foo

【问题讨论】:

  • 可能你问的不是你要解决的真正问题,而是不相关的技术问题。根据您在下面提到的视图装饰器重新表述您的问题。装饰器不能访问类名,但它返回的函数可以。
  • 是的,你是对的。以后我会尽量讲清楚。
  • 我已经编辑了我的帖子以添加我的实际代码。
  • @yourfriendzak。 self 将永远无法在方法之外调用。你想使用元类。你用的是哪个版本的python?
  • 我可以使用元类来获取类名吗?

标签: python


【解决方案1】:

注意self 在您希望类引用自身以使作业正常工作时未定义。这是因为(除了被任意命名之外)self 指的是实例而不是类。在可疑代码行尝试运行时,还没有可以引用的类。并不是说它引用该类(如果有的话)。

在方法中,您始终可以使用type(self)。这将获得创建当前实例的MyClass 的子类。如果您想硬编码为MyClass,则该名称将在方法的全局范围内可用。这将允许您执行您的示例所允许的所有操作(如果它确实有效)。例如,您可以在您的方法中执行MyClass.some_attribute inside

您可能希望在创建类之后修改类属性。这可以使用装饰器或临时完成。元类可能更合适。但是,如果不知道您实际上想做什么,就不可能说出来。

更新:

这里有一些代码可以做你想做的事。它使用元类AutoViewConfigMeta 和一个新的装饰器来标记您希望view_config 应用于的方法。我欺骗了view_config 装饰器。它在被调用时打印出类名,以证明它可以访问它。元类__new__ 只是循环遍历类字典并查找由auto_view_config 装饰器标记的方法。它清除标记并应用view_config 装饰器 适当的类名。

这是代码。

# This just spoofs the view_config decorator.
def view_config(route=''):
    def dec(f):
        def wrapper(*args, **kwargs):
            print "route={0}".format(route)
            return f(*args, **kwargs)
        return wrapper
    return dec

# Apply this decorator to methods for which you want to call view_config with 
# the class name. It will tag them. The metaclass will apply view_config once it 
# has the class name. 
def auto_view_config(f):
    f.auto_view_config = True
    return f

class AutoViewConfigMeta(type):
    def __new__(mcls, name, bases, dict_):
        #This is called during class creation. _dict is the namespace of the class and
        # name is it's name. So the idea is to pull out the methods that need
        # view_config applied to them and manually apply them with the class name.
        # We'll recognize them because they will have the auto_view_config attribute
        # set on them by the `auto_view_config` decorator. Then use type to create
        # the class and return it.

        for item in dict_:
            if hasattr(dict_[item], 'auto_view_config'):  
                method = dict_[item]
                del method.auto_view_config # Clean up after ourselves.
                # The next line is the manual form of applying a decorator.
                dict_[item] = view_config(route=name)(method)  

        # Call out to type to actually create the class with the modified dict.
        return type.__new__(mcls, name, bases, dict_)


class simpleObject(object):
    __metaclass__ = AutoViewConfigMeta 


class Test(simpleObject):

    @auto_view_config
    def activateTheView(self):
        foo = 'bar'

        print foo

if __name__=='__main__':
    t = Test()
    t.activateTheView()

如果您有任何问题,请告诉我。

【讨论】:

  • 我想从类中获取类名,比如 self.__class__.__name__
  • @yourfriendzak,你可以在 type(self).__name__ 的方法中做到这一点。如果你想让它成为一个类属性,那么它已经是类创建之后,即,它是__class__ 类属性。如果您不想获取当前子类的名称,那么只需将其硬编码,但那有什么意义呢?
  • 我听到了,但我想做的是调用 type(self).__name__ OUTSIDE 的方法。我希望 type(self).__name__ 作为类属性而不必先创建该类的实例,这显然是不可能的。
  • @yourfriendzak。为什么你希望MyClass 有一个附加的类属性来引用它的名字?它已经具有__name__ 属性。为什么这还不够?
  • 如果我没有“self”(self.__name__ 抛出错误),我如何引用 name 属性?我想要一种方法,我不必在类本身内部硬编码类名,这样我就可以轻松地重用代码。
【解决方案2】:

Python 有“显式优于隐式”的设计理念。

许多语言在方法(例如 C++ 中的 this)的范围内都有一个隐式指针或变量,该指针或变量指向调用该方法的对象。 Python没有这个。在这里,所有绑定的方法都会有一个额外的第一个参数,即调用该方法的对象。您可以随意调用它(self 不是 C++ 中的 this 之类的关键字)。名称self 是约定而不是句法规则。

您的方法myFunction 将变量self 定义为参数,因此它可以工作。在类级别没有这样的变量,所以它出错了。

解释就这么多。我不知道有一种简单的方法可以让你做你想做的事,而且我从未在 Python 中看到过这样的要求。你能详细说明你为什么要做这样的事情吗?也许有一个假设,您正在做出可以使用 Python 以另一种方式处理的假设。

【讨论】:

  • 我正在尝试定义一个可以重复使用的顶级类“模板”。我在类定义中有一个装饰器,就像 @view_config(route_name='myClass') 我希望能够不必使用 myClass 字,以便将值自动映射到 @view_config 所在的类的名称
  • 我不确定您如何做到这一点。但是,听起来您想根据运行时参数在类中创建属性。也许创建类的元类或某种工厂会比您从中子类化其他人的“模板”更好。
  • @yourfriendzak 请使用有关此装饰器及其作用的信息更新答案。这更接近您想要实现的目标。
【解决方案3】:

self 只是一个名称,在这种情况下,您的 self 是一个类变量,而不是用于调用它的对象的 this,

self 被视为普通变量,未定义,因为函数中的 self 来自用于调用的对象。

您想将self 中的对象引用视为不可能的类变量。

【讨论】:

    【解决方案4】:

    self 不是关键字,它只是一个约定。方法是类对象(不是实例)的属性,但它们接收实例作为它们的第一个参数。如果需要,您可以将参数重命名为 xyzzy,它仍然会以相同的方式工作。

    但是(应该很明显)您不能在方法体之外引用方法参数。在 class 块内但在任何方法之外,self 是未定义的。而且这个概念甚至没有意义——在评估 class 块时,可能还没有该类的实例。

    【讨论】:

    • 我的问题不在于特定的单词“self”(我知道这是任意的)。问题是我想引用类本身中的一个实例(通常由单词'self'表示),而不必在专门定义的函数中使用它。
    • @yourfriendzak:查看我的编辑。我认为您误解了 class 关键字的工作原理。评估class 块的主体以形成类属性的__dict__。在 Python 中,一个类与任何其他类一样只是一个对象,只是它实现了 __call__ 特殊功能,以便调用类对象返回该类的新实例。在class 块的上下文中,根本没有任何可以引用的实例,即使self 可用。
    【解决方案5】:

    因为名称 self 已明确定义为 myFunction 的参数的一部分。方法的第一个参数是调用该方法的实例;在类体中,没有“我们正在处理的实例”,因为类体处理每个类的可能实例(包括那些不一定存在的实例) -因此,没有可以称为self 的特定对象。

    如果您想引用类本身,而不是它的某个实例,则拼写为self.__class__(或者,对于 Py2 中的新型类和 Py3 中的所有类,@ 987654326@) self 存在的任何地方。如果您希望能够在self 存在的情况下处理此问题,那么您可能需要查看与任何特定实例无关的class methods,因此用类本身代替self。如果您真的需要在类主体中执行此操作(并且您可能不需要),您只需按名称调用它。

    【讨论】:

      【解决方案6】:

      您不能在类主体中引用类本身,因为在执行类主体时该类不存在。 (如果前面的句子令人困惑,阅读有关元类的内容要么清楚这一点,要么让你更加困惑。)

      在实例方法中,您可以使用self.__class__ 引用实例的类,但这里要小心。这将是实例的实际类,通过继承的力量,它可能不是定义方法的类。

      在类方法中,类作为第一个参数传入,就像实例是实例方法的第一个参数一样:

      class MyClass(object):
          @classmethod
          def foo(cls):
              print cls.__name__
      
      MyClass.foo() # Should print "MyClass"
      

      与实例方法一样,实际类可能会因继承而有所不同。

      class OtherClass(MyClass): 
          pass
      
      OtherClass.foo() # Should print "OtherClass"
      

      如果你真的需要在 MyClass 的方法中引用 MyClass,除非你使用魔法,否则你几乎必须将其引用为 MyClass。这种魔法麻烦多于它的价值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-08-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多