2013-11-23 31 views
5

我有一个简单的类,它扩展长时间接受值调节剂的字符串(如“10米”是1024 * 1024 * 10)为什么Python的通话__str__而不是返回长值

我有__str__功能该打印传入的原始值(即,如果“10米”中传递,回报“10米”)

问题是,当我打电话的东西,如:

>>> printf("%d" % Size('10m')) 

我得到以下

SystemError: ../Objects/stringobject.c:4044: bad argument to internal function 

显然,如果我打印"%s"我得到“10米”

所以现在的问题是,因为我继承长,为什么高级应召__str__当它应该得到的长期价值。

顺便说一句,多一点测试表明,%x%f将打印整数值,让我更加困惑。我也尝试添加__format__,但似乎只在调用"...".format()时才被调用。

编辑#1,下面的代码:

class Size(long): 
    '''Represents a size reflected bytes. Subclass of long. 
    Size passed in must be in the formats <int> or "0x<int>" or "0x<int><unit>" or "<int><unit>" or "<int><unit><int><unit>....". 
    "0x<int><unit>0x<int><unit>" or similar numbers are not supported as is "<int><unit><int>" 

    b = bytes 
    s = sectors (512-byte) 
    k = kilobytes 
    m = megabytes 
    g = gigabytes 
    t = terabytes 
    ''' 

    units = { 'b':1, 's':512, 'k':1024, 'm':1024 ** 2, 'g':1024 ** 3, 't':1024 ** 4 } 

    def __new__(cls, value): 
     '''Creates a Size object with the specified value. 

     Value can be a number or a string (optionally prefixed with '0x' or 
     postfixed with a type character). If using hex, the final character 
     will be treated as part of the value if it is a hex digit, regardless 
     of whether it is a valid unit character. 

     Examples: 
      Size(50) 
      Size("0x100s") # 256 sectors 
      Size("64") 
      Size("512k") 
      Size("0x1b") # this is 1b bytes, not 1 byte 
     ''' 
     self = _new_unit_number(value, cls.units, long, cls) 
     return self 

    def __init__(self, value): 
     self._orig_value = value 

    def __str__(self): 
     print "calling str" 
     return str(self._orig_value) # Convert to str in case the object was created w/an int 

    def __format__(self, format_spec): 
     print "calling format" 
     print format_spec 
     try: 
      value = format(str(self), format_spec) 
     except ValueError: 
      value = format(int(self), format_spec) 
     return value 

def _new_unit_number(value, unit_list, num_type, cls): 
    '''Converts a string of numbers followed by a unit character to the 
    requested numeric type (int or long for example). 
    ''' 
    base = 10 
    start = 0 
    digits = string.digits 
    try: 
     if value[0:2] == '0x': 
      start = 2 
      base = 16 
      digits = string.hexdigits 

     if value[-1] in digits: 
      return num_type.__new__(cls, value[start:], base) 
     else: 
      try: 
       # Use a regex to split the parts of the unit 
       regex_string = '(\d+[%s])' % (''.join(unit_list.keys())) 
       parts = [x for x in re.split(regex_string, value[start:]) if x] 

       if len(parts) == 1: 
        return num_type.__new__(cls, num_type(value[start:-1], base) * unit_list[value[-1]]) 
       else: 
        # Total up each part 
        # There's probably a better way to do this. 
        # This converts each unit to its base type, stores it in total, 
        # only to be converted back to the base type. 
        total = 0 
        for part in parts: 
         total += num_type(part[start:-1], base) * unit_list[part[-1]] 

        # Finally return the requested unit 
        return num_type.__new__(cls, total) 
      except KeyError: 
       raise ValueError("Invalid %s unit identifier: %s" 
        % (cls.__name__, unit_list[value[-1]])) 

    # not a string or empty, see if we can still use the class's constructor 
    except (TypeError, IndexError): 
     return num_type.__new__(cls, value) 
+5

“尺寸”是如何定义的? – Hyperboreus

+2

请注意,“m”是米的国际符号,或10^-3的前缀。如果你想10^6,使用M.如果你想要2^20,使用Mi。 –

+1

你从Python 2.7中获得'printf()'函数的位置? –

回答

2

不是一个真正的答案,但过长的注释。

我觉得这个问题非常有趣。我试图复制使用此行为:

#! /usr/bin/python2.7 

class Size (long): 
    def __new__ (cls, arg): 
     if arg and type (arg) == str: 
      if arg [-1] == 'm': 
       return super (Size, cls).__new__ (cls, long (arg [:-1]) * 2 ** 20) 
     return super (Size, cls).__new__ (cls, arg) 

    def __init__ (self, arg): 
     self.s = arg 

    def __str__ (self): 
     return self.s 

a = Size ('12m') 
print (a) 
print ('%s' % a) 
#The following fails horribly 
print ('%d' % a) 

行为描述由OP。但现在来的有趣的部分:当我从int,而不是从长继承,工作平稳:

class Size (int): 
    def __new__ (cls, arg): 
     if arg and type (arg) == str: 
      if arg [-1] == 'm': 
       return super (Size, cls).__new__ (cls, int (arg [:-1]) * 2 ** 20) 
     return super (Size, cls).__new__ (cls, arg) 

    def __init__ (self, arg): 
     self.s = arg 

    def __str__ (self): 
     return self.s 

也就是说,它在python2工作正常,但在python3失败。奇怪,奇怪。

+1

你基本上有我写的类(我只支持更多的修饰符像K,m,g等),那就是确切的问题。有趣的是,它失败了很长时间,但不是int ...我需要很长的时间,因为我们超越了int支持的32位。 – JasonAUnrein

2

请参阅Python的问题跟踪,Issue 18780: SystemError when formatting int subclass

>>> class I(int): 
... def __str__(self): 
...  return 'spam' 
... 
>>> '%d' % I(42) 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
SystemError: Objects/unicodeobject.c:13305: bad argument to internal function 

这个工作在3.4.0alpha4,但不是在3 [0123]。

+0

注意:该代码适用于Python 2.7(用于'int'子类)。它打破了“长”的子类。 – jfs

相关问题