2013-11-23 2 views
5

У меня есть простой класс, который расширяет долго, чтобы принять строку с модификаторами значений (т.е. «10m» будет 1024 * 1024 * 10)Почему Python вызов __str__ вместо возвращения длинного значения

У меня есть функция __str__ который печатает исходное значение, переданное в (то есть, если «10m» передается в, возвращение «10m»)

Проблема заключается в том, что, когда я называю что-то такое, как:

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

Я получаю следующее

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

Очевидно, что если я печатаю "%s" я получаю «10m»

Таким образом, вопрос, так как я подклассов долго, почему класс вызова __str__, когда он должен получать длинные значения.

BTW, немного больше испытаний показывает, что %x и %f напечатает целочисленное значение, которое меня смущает больше. Я также попытался добавить __format__, но, похоже, вызывается только при вызове "...".format().

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

Где вы получаете функцию 'printf()' из Python 2.7? –

ответ

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) 

Поведение, описанное ОП. Но теперь приходит забавная часть: Когда я унаследовать от межд, а не от долго, она работает гладко:

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 .... Мне нужно, так как мы выходим за пределы 32 бит, которые поддерживает int. – 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'). Он ломается для 'long' подкласса. – jfs

Смежные вопросы