2013-11-23 13 views
5

Tôi có một lớp đơn giản kéo dài thời gian để chấp nhận một chuỗi với từ bổ nghĩa giá trị (ví dụ: '10m' sẽ là 1024 * 1024 * 10)Tại sao Python gọi __str__ thay vì trả lại giá trị lâu

Tôi có __str__ chức năng mà in các giá trị ban đầu được thông qua năm (tức là nếu '10m' được thông qua tại, trở lại '10m')

vấn đề là khi tôi gọi một cái gì đó như:

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

tôi nhận được sau

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

Rõ ràng nếu tôi in "%s" tôi nhận được '10m'

Vì vậy, câu hỏi là, kể từ khi tôi đang subclassing dài, tại sao cuộc gọi lớp __str__ khi nó phải được nhận được giá trị lâu.

BTW, thử nghiệm nhiều hơn một chút cho thấy rằng %x%f sẽ in giá trị số nguyên gây nhầm lẫn cho tôi nhiều hơn. Tôi cũng đã thử thêm __format__ nhưng dường như chỉ được gọi khi số điện thoại "...".format() được gọi.

EDIT # 1, Dưới đây là các mã:

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

'Kích thước' được xác định như thế nào? – Hyperboreus

+2

Lưu ý rằng "m" là ký hiệu quốc tế cho đồng hồ, hoặc tiền tố cho 10^-3. Nếu bạn muốn 10^6, sử dụng M. Nếu bạn muốn 2^20, sử dụng Mi. –

+1

Bạn đang nhận hàm 'printf()' trong Python 2.7 ở đâu? –

Trả lời

2

Không thực sự là một câu trả lời, nhưng quá dài cho một nhận xét.

Tôi thấy câu hỏi này rất thú vị. Tôi đã cố gắng sao chép hành vi bằng cách sử dụng điều này:

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

Hành vi như được mô tả bởi OP. Nhưng bây giờ đến phần hài hước: Khi tôi kế thừa từ int và không phải từ lâu, nó hoạt động trơn tru:

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 

Nghĩa là, nó hoạt động tốt trong python2, nhưng thất bại trong python3. Lạ thật, kỳ lạ.

+1

Bạn về cơ bản có các lớp học mà tôi đã viết (tôi chỉ hỗ trợ nhiều modifier như K, m, g, vv) và đó là vấn đề chính xác. Thú vị là nó không thành công lâu nhưng không phải trên int .... Tôi cần lâu kể từ khi chúng tôi vượt quá 32bits int hỗ trợ. – JasonAUnrein

2

Xin xem Python theo dõi vấn đề, 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 

này hoạt động trong 3.4.0alpha4, nhưng không phải trong 3. [0123].

+0

lưu ý: mã hoạt động trên Python 2.7 (đối với lớp con 'int'). Nó phá vỡ cho lớp con 'long'. – jfs

Các vấn đề liên quan