2013-11-23 10 views
5

Mam proste klasy, która rozciąga się długi, aby zaakceptować ciąg z modyfikatorów wartości (czyli „10m” byłby 1024 * 1024 * 10)Dlaczego Python rozmowę __str__ zamiast zwracania wartości długiej

Mam funkcję __str__ która drukuje oryginalne wartości przekazanej w (czyli jeśli „10m” jest przekazywana, zwrot „10m”)

problem jest to, że gdy zgłoszę coś takiego jak:

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

otrzymuję następujący

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

Oczywiście, jeśli mogę wydrukować "%s" otrzymuję „10m”

Więc pytanie jest, ponieważ jestem instacji długo, dlaczego rozmowa klasa __str__ gdy powinna być coraz długiej wartość.

BTW, trochę więcej testów pokazuje, że %x i %f wydrukują wartość całkowitą, co może mnie bardziej zdezorientować. Próbowałem również dodać __format__, ale wydaje się, że jest on wywoływany tylko wtedy, gdy wywoływana jest "...".format().

EDIT # 1, Oto kod:

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

Jak zdefiniowano "Rozmiar"? – Hyperboreus

+2

Należy zauważyć, że "m" jest międzynarodowym symbolem licznika lub prefiksem dla 10^-3. Jeśli chcesz 10^6, użyj M. Jeśli chcesz 2^20, użyj Mi. –

+1

Skąd czerpiesz funkcję 'printf()' w Pythonie 2.7? –

Odpowiedz

2

Naprawdę nie odpowiedź, ale zbyt długo na komentarz.

Uważam to pytanie za bardzo interesujące. Próbowałem odtworzyć zachowanie za pomocą tego:

#! /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) 

Zachowanie opisane przez OP. Ale teraz jest zabawną część: Kiedy dziedziczą z int, a nie od dawna, że ​​działa sprawnie:

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 

Oznacza to, że działa dobrze w python2, ale nie trafia w python3. Dziwne, dziwne.

+1

Zasadniczo masz klasę, którą napisałem (moja obsługuje tylko więcej modyfikatorów, takich jak K, m, g, itd.) I to jest dokładny problem. Interesujące, że kończy się niepowodzeniem na długo, ale nie na int .... Potrzebuję długo, ponieważ wychodzimy poza 32-bitowe, które obsługuje int. – JasonAUnrein

2

proszę zobaczyć Python śledzenia błędów, 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 

to działa w 3.4.0alpha4, ale nie w 3. [0123].

+0

Uwaga: kod działa w języku Python 2.7 (dla podklasy 'int'). Przerywa dla podklasy 'long'. – jfs

Powiązane problemy