Python中的元类和语法

Python中的元类和语法,python,class,syntax,Python,Class,Syntax,我试着做这样的事情: 类对象(对象): 定义初始化(self,x=0,y=0,z=0): self.x=x self.y=y self.z=z def关联字符串(自身,值): 返回str(值) 向量=对象(5,5,5) #所以我能做到 关联(vector.x) #但是我想要这种语法 vector.x.asString() 这只是一个例子,我真的不想把整数转换成字符串。这更多的是关于类到类的问题。在Python中不能做这种事情 但是,您可以在类中实现标准的\uuuu str\uuu方法,这是使用

我试着做这样的事情:

类对象(对象):
定义初始化(self,x=0,y=0,z=0):
self.x=x
self.y=y
self.z=z
def关联字符串(自身,值):
返回str(值)
向量=对象(5,5,5)
#所以我能做到
关联(vector.x)
#但是我想要这种语法
vector.x.asString()
这只是一个例子,我真的不想把整数转换成字符串。这更多的是关于类到类的问题。

在Python中不能做这种事情

但是,您可以在类中实现标准的
\uuuu str\uuu
方法,这是使用
str(instance)
将实例转换为字符串时将使用的代码

从技术上讲,您可以在python中玩很多把戏,尝试将语法弯曲到您习惯使用的任何语法,但这是一个坏主意,因为在使python更具可读性方面付出了很多努力,而您基本上正在破坏这项工作

在Python中,到字符串的转换由
str(x)
完成,而不是调用名为
asString
的方法。使用
\uuuu str\uuuu
您已经可以自定义
str
将返回的内容,为什么要添加方法?如果您需要一种方法来进行自定义字符串转换,那么只需在对象类型上定义函数分派,而不是尝试在现有类上注入新方法:

converters = dict()

def add_converter(klass, f):
    converters[klass] = f

def default_converter(x):
    return "<%s: %s>" % (x.__class__.__name__, str(x))

def mystr(x):
    return converters.get(x.__class__, default_converter)(x)
converters=dict()
def添加_转换器(klass,f):
转换器[klass]=f
def默认_转换器(x):
返回“%”(x
def mystr(x):
返回转换器.get(x.。\uuuuuuuuuuuuuuuuuuuuuuuuuuu类,默认的\uu转换器)(x)
使用这种方法没有“神奇”(即令人惊讶)的行为,也没有包装东西(另一种方法可能会让阅读代码的人感到惊讶)

在上面的示例中,我没有处理转换器继承,但是如果需要并且确实需要,您可以通过使用更复杂的查找来实现这一点(不确定继承到字符串函数的转换是否有意义,它会自动丢失信息)

此外,如果您不理解元类的用途,请不要理会这个概念,很可能您并不真正需要它。元类是一个功能强大但有点复杂的工具,实际上并不经常需要它


我认为这是对什么是元类以及如何使用元类的一个很好的一般性解释。请注意,缺少一些血淋淋的细节,您应该使用官方文档来挖掘它们。

您可以为
oObject
类编写一个自定义方法,返回给定
键的字符串,或者您可以编写一个自定义
Variant
类并包装您的值:

class oObject(object):
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = Variant(x)
        self.y = Variant(y)
        self.z = Variant(z)

class Variant(object):
    def __init__(self, obj):
        self._obj = obj

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, self.asString())

    def __str__(self):
        return self.asString()

    def asString(self):
        return str(self._obj)

    def value(self):
        return self._obj
类对象(对象):
定义初始化(self,x=0,y=0,z=0):
self.x=变量(x)
self.y=变量(y)
self.z=变量(z)
类变量(对象):
定义初始(自我,对象):
自身。_obj=obj
定义报告(自我):
返回“”%(self.\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu名称,self.asString())
定义(自我):
返回self.asString()
def关联(自):
返回str(自我检查)
def值(自身):
返回自我

查看此参考资料,了解PyQt4是如何使用的,它实际上来自Qt。通常Python不需要这种类型,但是C++有必要代表多种类型。 在Python中,想要得到你想要的东西是很棘手的- 那是因为,当你 “instance.x.method”-Python首先从“instance”中检索属性“x”,然后 它将尝试在“x”对象本身中找到“method”作为属性(不引用“instance”,该“instance”最初引用了“x”,可以从“method”内部检索到,但用于帧内省)

我说过它“可以做到”——并且可以用于大多数类型的x,但最终可能会失败,或者产生折叠效果,并与属性“x”的类型无关: 如果为类编写
\uuuu setattr\uuuu
方法,则对于实例上的每个属性集,它实际上会创建该属性的动态子类,从而在新对象上启用所需的方法。退一步说,并不是所有类型的对象都可以被子类化,也不是所有子类对象的行为都与它们的父对象完全相同。(例如,如果“x”是一个函数)。但这在大多数情况下都是可行的:

class Base(object):
    def __setattr__(self, name, attr):
        type_ = type(attr)
        new_dict = {}
        for meth_name in dir(self.__class__):
            function = getattr(self.__class__, meth_name)
            # Assume any methods on the class have the desired behavior and would
            # accept the attribute as it's second parameter (the first being self).

            # This could be made more robust by making a simple method-decorator
            # which would mark the methods that one wishes to be appliable
            # to attributes, instead of picking all non "_" starting methods like here:

            if not callable(function) or meth_name in new_dict or meth_name.startswith("_"):
                continue
            def pinner(f):
                def auto_meth(se, *args, **kw):
                    return f(se._container, se, *args, **kw)
                return auto_meth
            new_dict[meth_name] = pinner(function)
        # This could be improved in order to have a class-based cache of derived types
        # so that each attribute setting would only create a new_type for
        # each different type that is being set
        new_type = type(type_.__name__, (type_,), new_dict)
        try:
            attr.__class__ = new_type
        except TypeError:
            # here is the main problem withthis approach: 
            # if the type being stored can't have it's `__class__`dynamically
            # changed, we have to build a new instance of it.
            # And if the constructor can't take just the base type
            # as its building parameter, it won't work. Worse if having another instance
            # does have side-effects in the code, we are subject to those.

            attr = new_type(attr)
        attr._container = self
        super(Base, self).__setattr__(name, attr)



class oObject(Base):
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = x
        self.y = y
        self.z = z

    def asString(self, attr):
        return str(attr)
在交互式部分加载这些内容后:

>>> v = oObject(1,2,3)
>>> v.x.asString()
'1'
>>> v.w = [1,2,3]
>>> v.w.append(3)
>>> v.w.asString()
'[1, 2, 3, 4]'
>>> 
正如您所看到的,这可以通过正常的类继承来完成,而不需要元类

对于任何参数类型,另一种更可靠的方法是使用另一个分隔符作为属性名和方法-您可以在基类上编写一个更简单的
\uuuuuuu getattribute\uuuuuu
方法,该方法将动态检查请求方法并为属性调用它。这种方法不需要动态子分类,并且大约简单2个数量级。其代价是您可以编写类似于
vector.x\u asString
的内容,而不是使用点分隔符。这实际上是在经过尝试和测试的SQLALchemy ORM for Python中采用的方法

# Second approach:

class Base(object):
    separator = "__"
    def __getattr__(self, attr_name):
        if self.__class__.separator in attr_name:
            attr_name, method_name = attr_name.split(self.__class__.separator, 1)
            method = getattr(self, method_name)
            return method(getattr(self, attr_name))
        raise AttributeError
现在:

>>> class oObject(Base):
...     def __init__(self, x = 0, y = 0, z = 0):
...         self.x = x
...         self.y = y
...         self.z = z
...         
...     def asString(self, attr):
...         return str(attr)
... 
>>> 
>>> 
>>> v = oObject(1,2,3)
>>> v.x__asString
'1'

(如果希望将更多的参数传递给被调用的方法,则需要更多的代码,但我认为这就足够了)。

您正在谈论将自定义方法添加到内置
int
?如果这只是一个示例,并且您真正指的是对象,那么
x
可能是一个带有
asString
方法的自定义对象是的,我想添加动态自定义方法。不仅适用于x,也适用于y和z。但是我不知道怎么做。例如,你可以做一些像
vector.asString('x')
是的,也许你是对的,我不理解这个概念。我认为这需要一个元类…@jsbueno:我认为在Python中,没有合理的方法可以向数字添加一个新方法,而不让这个构造泄漏