【问题标题】:Python: In class Foo: x = MyClass() can MyClass know the name of the variable it is assigned to?Python:在 Foo 类中:x = MyClass() MyClass 可以知道它分配给的变量的名称吗?
【发布时间】:2013-02-02 22:50:54
【问题描述】:

我希望能够做到以下几点:

class PrintName:
    def __init__( self, obj ):
        print obj._name

class SetName:
    def __init__( self, name = None ): # Default name is None
        self._name = name

class Test:
    f = SetName( ) # No explicit name given
    g = PrintName( f )

此时我希望 python 打印 'f',所以在执行 PrintName(f) 时 f 应该知道它的名称。

我发现的每个自动命名解决方案都在创建后为属性命名。 我一直在尝试使用元类来解决这个问题,但即使这样似乎也不起作用。

我想这样做是为了能够“保存”和“重新加载”python 代码以供以后使用(有点像原始脚本语言,可以在使用时评估的程序中更改)

例如:

x = 0
y = 0
p = ( x, y )

打印 p 得到 (0,0),然后用 x 和 y 做一些事情,得到

x = 124
y = -32
p = ( x, y )

打印 p 变为 (124,-32)。

最简单的方法是使用

p = ( 'x', 'y' )

但是在这种情况下,我们怎么知道'x'是变量名而不是字符串'x'

我已经尝试为此目的编写一种简单的脚本语言,但工作量很大,如果上述方法可行,那么整个 python 语言都可以在脚本中使用。

我正在尝试为我的问题找到一个简单而灵活的解决方案。

提前感谢您的帮助。

【问题讨论】:

  • 此时我希望 python 打印 'f' - 这不正是它的作用吗?你到底想在这里做什么?你所有的代码示例都按照你说的去做
  • 一个Python对象可以有多个名字,它怎么知道你想要哪个?
  • x = MyClass(); y = x; print nameOf(y) - 那应该怎么做?
  • 更好的是,许多对象可能根本没有名字。考虑在评估[1] + [2] + [3] 期间创建的中间列表[1,2](每个列表和中间列表都是从VM 内部引用的,而不是通过任何Python 名称引用的)。注意你所追求的效果是在一种叫做反应式编程的范式中找到的,众所周知,它很难改造成命令式编程语言——如果你想以任何类似于“好”的方式做到这一点,你就必须做好准备并自己实施很多
  • 0) 将原始示例更改为我想要的。 1) 如果是:x = MyClass(); y = x;这应该创建一个具有不同名称的新对象。一个带有“x”,一个带有“y” 2) 匿名对象应该有一个生成的唯一名称。 (示例:MyClass000)

标签: python serialization metaclass


【解决方案1】:

我设法在 Python 3.x 中使用 __prepare__ 找到了解决方案。 这是一个解释我想要做什么的工作代码。

from collections import MutableMapping

class MDict(MutableMapping):
    def __init__(self, *args, **kwargs):
        self._d = dict(*args, **kwargs)
    def __getitem__(self, key):
        return self._d[key]
    def __setitem__(self, key, value):
        self._d[key] = value
        try:
            self._d[key]._key = key # Let's set the name of the object
        except AttributeError: # Not a good way to handle immutable objects
            pass
    def __delitem__(self, key):
        del self._d[key]
    def __iter__(self):
        return iter(self._d)
    def __len__(self):
        return len(self._d)

class MBase(type):
    @classmethod
    def __prepare__(metacls, name, bases, **kwargs):
        return MDict()
    def __new__(metacls, name, bases, mdct):
        return super().__new__(metacls, name, bases, dict(mdct))
    def __str__( self ):
        return "class {0}(CSub, metaclass=MBase):".format( self.__name__ )

class CSub: # An empty class so we know when to go deeper int print_code
    pass

class Integer:
    def __init__( self, value ):
        self._value = value
    def __str__( self ):
        try: # See if it's a reference
            return "{0} = Integer( {1} )".format( self._key, self._value._key )
        except: # Not it's a number
            return "{0} = Integer( {1} )".format( self._key, self._value )

class Point:
    def __init__( self, x, y ):
        if type( self ) == type( x ): # Check to see if initializing with a reference
            self._x, self._y = x._key, y._key
        else: # It's not reference
            self._x, self._y = x, y
    def __str__( self ):
        try:
            return "{0} = Point( {1}, {2} )".format( self._key, self._x._key, self._y._key )
        except:
            return "{0} = Point( {1}, {2} )".format( self._key, self._x, self._y )


def print_code( cls, indent = 2 ):
    # Print the header
    if indent == 2:
        print( "class Main(metaclass=MBase):" )
    for attr, value in cls.__dict__.items():
        if not attr.startswith( "_" ): # Ignore hidden attributes
            print( " " * indent + str( value ) ) # Use indentation
            if isinstance( value, CSub.__class__ ): # If it's a subclass then process that too
                print_code( value, indent + 2 )

class Main(metaclass=MBase):
    x = Integer( 0 )
    y = Integer( 100 )
    z = Integer( x )
    p1 = Point(x,x)
    p2 = Point(y,y)
    class Sub(CSub, metaclass=MBase):
        p = Point(1,1)
print_code( Main )

这样,如果我更改对象 x,那么所有其他引用 x 的对象也会更改。 更重要的是,我可以轻松地将代码保存到文本文件中以供以后使用。

这仍然需要改进,但这是一个好的开始。 我希望这会帮助其他人寻找类似的东西。

【讨论】:

    【解决方案2】:

    不可能从一个对象向后工作到保存它的变量的名称,除非通过迭代当前上下文中的每个变量并检查哪些变量(如果有)具有等于给定对象的值.那会相对缓慢和混乱。

    也许更好的选择(虽然我不确定它是否适合您的目的)将使用 name 本身,例如而不是将f 传递给PrintName(或其他),而是传递'f',字符串。如果您需要访问 PrintName 构造函数中的变量值,则可以访问当前堆栈帧上方的堆栈帧并选择具有给定名称的变量。您可以使用inspect module 来执行此操作,如下所示:

    class PrintName:
        def __init__(self, name):
            # of course you can just print the name
            last_frame = inspect.stack()[1]
            if name in last_frame.f_locals:
                value = last_frame.f_locals[name]
            elif name in last_frame.f_globals:
                value = last_frame.f_globals[name]
            # do stuff with value
    

    不用说,这是相当老套的,而不是你应该在任何正常程序中做的事情。它旨在用于调试器之类的东西。

    【讨论】:

      猜你喜欢
      • 2018-10-17
      • 2020-07-21
      • 2014-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多