3

我试着做这样的事情:

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

def asString (self, value):
    return str(value)

vector = oObject(5,5,5)

# So i can do
asString(vector.x)

# But I want this kind of syntax
vector.x.asString()

这只是一个例子,我真的不想将整数转换为字符串。它更多的是关于课堂的课堂。

4

3 回答 3

3

您可以为您的oObject类编写一个返回给定字符串的自定义方法key,或者您可以编写一个自定义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

查看此参考资料,了解 PyQt4 如何使用QVariant 类(实际上来自 Qt)。通常 python 不需要这种类型,但 C++ 需要表示多种类型。

于 2012-07-08T18:38:43.437 回答
2

不能不应该在 Python 中做这种事情。

但是,您可以做的是__str__在类中实现标准方法,这是在使用str(instance).

从技术上讲,你可以在 python 中玩很多花样,试图将语法弯曲到你习惯的任何东西,但这是一个坏主意,因为已经付出了很多努力来使 Python 更具可读性,而你基本上是在破坏这项工作。

在 Python 中,转换为字符串是由 完成的str(x),而不是通过调用名为asString. 使用__str__您已经可以自定义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)

使用这种方法没有“神奇”(即令人惊讶的)行为,并且您没有包装东西(另一种可能会让阅读代码的人感到惊讶的方法)。

在上面的例子中,我没有处理转换器继承,但是如果你需要并且如果你真的想要的话,你可以通过使用更复杂的查找来做到这一点(不确定继承转换为字符串函数是否有意义,它会默默地丢失信息)。

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

我认为这篇文章很好地解释了元类是什么以及你可以用它们做什么。请注意,缺少一些血腥细节,您应该使用官方文档来挖掘它们。

于 2012-07-08T18:27:45.350 回答
0

在 Python 中准确地得到你想要的东西是很棘手的——那是因为,当你做“instance.x.method”时——Python 首先从“instance”中检索属性“x”,然后它们会尝试找到“method” " 作为“x”对象本身的一个属性(没有对“实例”的任何引用,该“实例”最初具有对“x”的引用,可以从“方法”内部检索到 - 但用于框架内省)。

我说它“可以完成” - 并且适用于大多数类型的 x,但最终可能会失败或产生附带影响,具体取决于属性“x”的类型:如果你 __setattr__为你的类编写一个方法对于实例上设置的每个属性,它实际上会创建该属性的动态子类——这将在新对象上启用所需的方法。缺点是并非所有类型的对象都可以被子分类,并且并非所有子分类对象的行为都与它们的父对象完全相同。(例如,如果“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]'
>>> 

正如你所看到的,这可以通过普通的类继承来完成,不需要元类。

对于任何 Parameter 类型,另一种更可靠的方法是对属性名称和方法使用另一个分隔符 - 您可以__getattribute__在基类上编写一个更简单的方法,该方法将动态检查请求方法并调用它属性。这种方法不需要动态子分类,并且简单了大约 2 个数量级。代价是你会写一些类似的东西vector.x__asString 而不是点分隔符。这实际上是在用于 Python 的久经考验的 SQLALchemy ORM 中采用的方法。

# 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'

(如果您希望将更多参数传递给被调用的方法,则需要更多代码,但我认为这足以理解这个想法)。

于 2012-07-08T20:49:49.667 回答