I have this python file that I'm working on:
class Range:
""" An object that has a non-negative start position and a non-negative length"""
def __init__(self, start, end):
"""
Function: generates a new Range
Returns : a new range
Args : start - start position
end - end position
Start and End must be non-negative """
self.start = 0
self.end = 10000
self.setStart(start)
self.setEnd(end)
def getStart(self):
"""
Function: get the start of the range
Returns : a number or None
Args : start - the int index of the first nucleotide of this range """
return self.start
def setStart(self, s):
"""
Function: set the start of the range
Returns : None
Args : start - a non-negative int or None """
if type(s) !=int:
raise TypeError ("Cannot set Start as this is not an interger")
elif s < 0:
raise ValueError ("Cannot set Start as this is not a non-negative int")
elif s > self.end:
raise ValueError("start cannot be larger than end")
else:
self.start = s
def getEnd(self):
"""
Function: get the end of the range
Returns : a number
Args :
"""
return self.end
def setEnd(self, e):
"""
Function: set the end of the range
Returns : None
Args : end - a non-negative int or None
"""
if type(e) !=int:
raise TypeError ("Cannot set End as this is not an interger")
elif e < 0:
raise ValueError ("Cannot set End as this is not a non-negative int")
elif e < self.start:
raise ValueError ("end has to be larger than start")
else:
self.end = e
def getLength(self):
"""
Function: get the length of the range
Returns : an int. the length of this range
Args:
"""
return self.end - self.start
def overlaps(self, r):
"""
Function: to test if two nucleotide is overlap
Returns : True or False
Args : other - a Range object
"""
start1 = self.getStart()
end1 = start1 + self.getLength()
start2 = r.getStart()
end2 = start2 + self.getLength()
max_start = max(start1,start2)
min_end = min(end1,end2)
return min_end - max_start > 0
if self.getStart() == r.getStart():
return True
else:
return False
class DNAFeature(Range):
"""Represents a feature on a DNA sequence """
def __init__(self, seq_name = None, strand = 0, **kwargs):
"""
Function : represents a rane
Returns :
Args : strand, seqname, **kwargs
"""
Range.__init__(self, **kwargs)
self.setStrand(strand)
self.setSeqName(seq_name)
def getSeqName(self):
"""
Function: Gets object's Sequence Name
Returns : seqname - string
Args :
"""
return self.seq_name
def setSeqName(self, seq_name):
"""
Function: Sets object's Sequence Name
Returns : None
Args : seqname - mRNA accession name
"""
self.seq_name = seq_name
def getStrand(self):
"""
Function: Retrieve the strand affiliation of this
Returns : 1, 0, -1 - strand
Args :
"""
return self.strand
def setStrand(self, strand):
"""
Function: sets which strand the object is on
Returns : None
Args : strand - one of ['+',1,'F','-',-1,'R']
"""
StrandValues = [1, 0, -1]
if not strand in StrandValues:
raise ValueError("only able to setStrand if the values is 1, 0, or -1")
else:
self.strand = strand
def overlaps(self, other, ignore_strand = True):
"""
Function: tests if this overlaps other
Returns : true if the ranges have same Seqname and overlap, false if not
Args : other - another Range object
"""
if ignore_strand == True and self.getSeqName() == other.getSeqName():
return Range.overlaps(self,other)
else:
return False
class GeneModel(DNAFeature):
def __init__(self, transl_start=None, transl_stop=None, display_id = None, **kwargs):
"""
Function : contains a group of DNAFeature objects representing exons
Returns :
Args : **kwargs
"""
DNAFeature.__init__(self, **kwargs)
self.setTranslStart(transl_start)
self.setTranslStop(transl_stop)
self.setDisplayId(display_id)
self.exons = [ ]
def getFeats(self):
"""
Function: gets object's feats list
Returns : list of feature keys
Args : feature_type - the type of strand the object holds
"""
self.exons.sort(cmp=self.start)
return self.exons
def addFeat(self, feat):
"""
Function: adds SeqFeature to feats keys
Returns : None
Args : feat - a single SeqFeature object
"""
if type(feat) == DNAFeature:
self.exons.append(feat)
else:
raise TypeError("Cannot add feature as it is not a type of DNAFeature")
def setTranslStart(self, transl_start):
"""
Function : accepts an non-negative int, sets the start position of the initiating ATG
Returns :
Args : transl_start
"""
if transl_start == None:
self.transl_start = None
return
elif type(transl_start) !=int:
raise TypeError("TranslStart cannot be set since it is not a type of int")
elif transl_start < 0:
raise ValueError("TranslStart cannot be set to a negative int")
else:
self.translStart = transl_start
def getTranslStart(self):
"""
Function: the start position of initiating ATG codon
Return : an int.
Args :
"""
return self.transl_start
def setTranslStop(self, transl_stop):
"""
Function: set the end position of initiating ATG codon
Return : None
Args : a positive int
"""
if transl_stop == None:
self.transl_stop = None
return
elif type(transl_stop) !=int:
raise TypeError("TranslStop cannot be set since it is not a type of int")
elif transl_stop < 0:
raise ValueError("TranslStop cannot be set to a negative int")
else:
self.translStop = transl_stop
def getTranslStop(self):
"""
Function: the end position of initiating ATG codon
Return : an int.
Args :
"""
return self.transl_stop
def setDisplayId(self, display_id):
"""
Function: set the display id
Returns : None
Args : display_id - a string, a preferred name for this
"""
if type(display_id) !=str:
raise TypeError("Cannot set displayId as it is not a type string")
else:
self.display_id = display_id
def getDisplayId(self):
"""
Function: get the display id
Returns : display_id - a string, a preferred name for this, e.g AT1G10555.1
Args :
"""
return self.display_id
Then, I got some code from my professor to test my file:
class TestGeneModelConstructor(unittest.TestCase):
def testGeneModelConstructor(self):
"""GeneModel constructor supports named arguments display_id,transl_start,transl_stop"""
p1.GeneModel(start=0,end=10,seq_name='something',strand=1,display_id='foobar',
transl_start=0,transl_stop=10)
def testGeneModelConstructorDefaults(self):
"""Default values for display_id, transl_start, transl_stop should be None"""
r = p1.GeneModel()
self.assertEquals(r.getDisplayId(),None)
self.assertEquals(r.getTranslStart(),None)
self.assertEquals(r.getTranslStop(),None)
def testGeneModelConstructorWrongTypeDisplayId(self):
"""Raise a TypeError if display_id is not a string."""
self.assertRaises(TypeError,p1.GeneModel,display_id=0)
def testGeneModelConstructorWrongTypeTranslStart(self):
"""Raise a TypeError if transl_start is not an int."""
self.assertRaises(TypeError,p1.GeneModel,transl_start='0')
def testGeneModelConstructorWrongTypeTranslStop(self):
"""Raise a TypeError if transl_stop is not an int."""
self.assertRaises(TypeError,p1.GeneModel,transl_stop='0')
def testGeneModelConstructorWrongValueTranslStart(self):
"""Raise a ValueError if transl_start is int < 0."""
self.assertRaises(ValueError,p1.GeneModel,transl_start=-1)
def testGeneModelConstructorWrongValueTranslStop(self):
"""Raise a ValueError if transl_stop is int < 0."""
self.assertRaises(ValueError,p1.GeneModel,transl_stop=-1)
I have run it and got these errors:
ERROR: Default values for display_id, transl_start, transl_stop should be None
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 117, in testGeneModelConstructorDefaults
r = p1.GeneModel()
TypeError: __init__() takes at least 3 arguments (1 given)
======================================================================
ERROR: Raise a ValueError if transl_start is int < 0.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 136, in testGeneModelConstructorWrongValueTranslStart
self.assertRaises(ValueError,p1.GeneModel,transl_start=-1)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 336, in failUnlessRaises
TypeError: __init__() takes at least 3 non-keyword arguments (2 given)
======================================================================
ERROR: Raise a ValueError if transl_stop is int < 0.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 140, in testGeneModelConstructorWrongValueTranslStop
self.assertRaises(ValueError,p1.GeneModel,transl_stop=-1)
File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/unittest.py", line 336, in failUnlessRaises
TypeError: __init__() takes at least 3 non-keyword arguments (1 given)
I'm not sure what is wrong, I have tried to fixed it couple times, but haven't figure out what is wrong in my codes.
Alright, I have change my code in DNAFeature like this:
class DNAFeature(Range):
"""Represents a feature on a DNA sequence """
def __init__(self, seq_name = None, strand = 0, **kwargs):
"""
Function : represents a rane
Returns :
Args : strand, seqname, **kwargs
"""
Range.__init__(self, 0,10000, **kwargs)
self.setStrand(strand)
self.setSeqName(seq_name)
And then, get 3 more errors and 1 failure like this:
ERROR: DNAFeature on different sequence don't overlap
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 164, in testDiffSequenceOverlaps
r1 = p1.DNAFeature(start=0,end=10,strand=1,seq_name="foo")
File "/Users/trungpham/binf_prog/tpham22/project1/p1.py", line 95, in __init__
Range.__init__(self, 0, 10000, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'start'
======================================================================
ERROR: DNAFeatures on the same strand can overlap if ignore_strand is True.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 185, in testDiffStrandsDontOverlap
r1 = p1.DNAFeature(start=0,end=10,strand=1,seq_name="foo")
File "/Users/trungpham/binf_prog/tpham22/project1/p1.py", line 95, in __init__
Range.__init__(self, 0, 10000, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'start'
======================================================================
ERROR: GeneModel constructor supports named arguments display_id,transl_start,transl_stop
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 113, in testGeneModelConstructor
transl_start=0,transl_stop=10)
File "/Users/trungpham/binf_prog/tpham22/project1/p1.py", line 151, in __init__
DNAFeature.__init__(self, **kwargs)
File "/Users/trungpham/binf_prog/tpham22/project1/p1.py", line 95, in __init__
Range.__init__(self, 0, 10000, **kwargs)
TypeError: __init__() got multiple values for keyword argument 'start'
FAIL: Raise a TypeError if seq_name is not a string.
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/trungpham/binf_prog/class/test/testProject1.py", line 98, in testDNAFeatureSeqNameConstructorWrongType
self.assertRaises(TypeError,p1.DNAFeature,seq_name=0)
AssertionError: TypeError not raised
There are several things wrong:
Firstly:
In your test function, testGeneModelConstructorDefaults, you have the comment: "Default values for display_id, transl_start, transl_stop should be None".
The problem you are seeing is that you have only set up the defaults on 1 of the 3 arguments; whereas this test function assumes you have set up defaults on all three. To fix this, you need to define the constructor like this:
def __init__(self, transl_start=None, transl_stop=None, display_id = None, **kwargs)
By having the keyword arguments, it will use the default of None. If you don't specify those keyword arguments explicitly, Python will complain (as it is currently doing).
Secondly:
You have to consider your superclass constructors. The Range superclass asks for two parameters: start and end.
def __init__(self, start, end):
However, when the DNAFeature constructor calls the Range superclass, it doesn't pass in begin or end:
Range.__init__(self, **kwargs)
This is where I think the error is now (I think before it was at the p1.GeneModel() call - there are probably different error lines in your two error messages).
To fix this, make the values for start and end into keyword parameters also. So instead of:
def __init__(self, start, end):
make it:
def __init__(self, start=0, end=10000):
You can then delete the following lines of code in your range constructor:
self.start = 0
self.end = 10000
That should at least get you past this error - you may find more errors that you have.
It appears that the tests are assuming that all parameters are passed by value. You need to give the positional arguments defaults.
Smashery is right. Try running this simplified code:
class GeneModel(object):
def __init__(self, transl_start=None, transl_stop=None, display_id = None, **kwargs):
pass
def testGeneModelConstructor():
g = GeneModel(start=0,end=10,seq_name='something',strand=1,display_id='foobar', transl_start=0,transl_stop=10)
def testGeneModelConstructorDefaults():
r = GeneModel()
testGeneModelConstructor()
testGeneModelConstructorDefaults()
Related
I am writing a small class 'AP' ('Arithmetic Progression') for my school assignment. I have included an option for making the AP infinitely long.
class AP:
def __init__(self, first_term: float, co_diff: float, n: int = inf) -> None:
self.a1 = first_term
self.d = co_diff
self.term = 0
self.n = n
def __repr__(self) -> str:
return f'AP({self.a1}, {self.d}, {self.n})'
def __str__(self) -> str:
output_str = f"{', '.join([str(term) for term in list(self.terms(term=6))])}, ...."
if self.n != inf:
return output_str + f', {(self.a1 + ((self.n - 1) * self.d))}'
return output_str
def terms(self, term: int):
return AP(self.a1, self.d, n=term)
def __iter__(self):
return self
def __next__(self):
self.term += 1
if self.term < self.n:
return (self.a1 + ((self.term - 1) * self.d))
raise StopIteration
def __eq__(self, o: object) -> bool:
if isinstance(o, AP):
return str(self) == str(o)
But when someone wants to convert it into a list and if it is an infinite one, I want to raise an error like:
ValueError: The list() function was called for an object of undefined length.
Same goes for sum() function as well. Are there any solutions to this?
AFAIK, Python has no notion of explicit conversion operator implemented in the source class (C++ has). The conversion is the responsibility of the destination class and currently list has no provision for infinity.
A possible workaround would be to hide list using a local function:
def list(it=()):
import builtins
if isinstance(it, AP) and it.n == inf:
raise ValueError('The list() function was called for an object'
' of undefined length.')
return builtins.list(it)
The limit is that if you use sorted which also returns a list, your special function will not be used.
Another limit, if your class is declared in a specific module and used from other modules is that the special function has to be imported in the other modules with from APmod import list
A possible trick would be to use the __length_hint__ special method. It is used at least in CPython for all standard conversions of an iterable to a sequence type, and AFAIK should only be used there. You cannot return a value to tell list that the length would be infinite but you can raise the ValueError directly:
def __length_hint__(self):
if self.n is inf:
raise ValueError('This object has undefined length.')
return self.n - 1
Example after adding this method to your class:
>>> ap = AP(1., 1.)
>>> ap
AP(1.0, 1.0, inf)
>>> next(ap) # iteration is correct
1.0
>>> next(ap)
2.0
>>> next(ap)
3.0
>>> next(ap)
4.0
>>> list(ap) # list raises ValueError
Traceback (most recent call last):
File "<pyshell#75>", line 1, in <module>
list(ap)
File "<pyshell#68>", line 34, in __length_hint__
raise ValueError('This object has undefined length.')
ValueError: This object has undefined length.
>>> tuple(ap) # so does tuple
Traceback (most recent call last):
File "<pyshell#76>", line 1, in <module>
tuple(ap)
File "<pyshell#68>", line 34, in __length_hint__
raise ValueError('This object has undefined length.')
ValueError: This object has undefined length.
>>> sorted(ap) # or sorted
Traceback (most recent call last):
File "<pyshell#77>", line 1, in <module>
sorted(ap)
File "<pyshell#68>", line 34, in __length_hint__
raise ValueError('This object has undefined length.')
ValueError: This object has undefined length.
But as long as you have a finite length, all is fine:
>>> ap = AP(1., 1., 4)
>>> list(ap)
[1.0, 2.0, 3.0]
By the way, you could use the same implementation for the special __len__ method to allow your class to be used by the len builtin function.
According to the documentation, this does not work because of this:
For custom classes, implicit invocations of special methods are only guaranteed to work correctly if defined on an object’s type, not in the object’s instance dictionary. That behaviour is the reason why the following code raises an exception:
>>> class C:
... pass
...
>>> c = C()
>>> c.__len__ = lambda: 5
>>> len(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'C' has no len()
https://docs.python.org/3/reference/datamodel.html#special-method-lookup
I had tried this on a function generator, which does not have __len__, but I knew beforehand its length, then, I tried monkey patch it with something like c.__len__ = lambda: 5, but it kept saying the generator object had no length.
This is the generator:
def get_sections(loaded_config_file):
for module_file, config_parser in loaded_config_file.items():
for section in config_parser.sections():
yield section, module_file, config_parser
I was passing the generator (which has no length) to this other function (yet, another generator), which requires the iterable length by calling len():
def sequence_timer(sequence, info_frequency=0):
i = 0
start = time.time()
if_counter = start
length = len(sequence)
for elem in sequence:
now = time.time()
if now - if_counter < info_frequency:
yield elem, None
else:
pi = ProgressInfo(now - start, float(i)/length)
if_counter += info_frequency
yield elem, pi
i += 1
https://github.com/arp2600/Etc/blob/60c5af803faecb2d14b5dd3041254ef00a5a79a9/etc.py
Then, when trying to add the __len__ attribute to get_sections, hence the error:
get_sections.__len__ = lambda: calculated_length
for stuff, progress in sequence_timer( get_sections ):
section, module_file, config_parser = stuff
TypeError: object of type 'function' has no len()
You can't add it to an existing object, so make your own wrapper class that has a class level definition you control:
class KnownLengthIterator:
def __init__(self, it, length):
self.it = it
self.length = int(length)
def __len__(self):
return self.length
def __iter__(self):
yield from self.it
Now you just change your invalid attempt to set a length of:
get_sections.__len__ = lambda: calculated_length
to a valid rewrapping that makes get_sections continue to be a valid generator (yield from will delegate all iteration behaviors to the wrapped generator), while exposing a length too:
get_sections = KnownLengthIterator(get_sections, calculated_length)
No other code needs to change.
You can do it in the following way
class A:
def __init__(self):
self.x = 1
a = A()
def b_fn(self):
return self.x
def __len__(self):
return 5
a.b = b_fn.__get__(a)
# A can be also obtained as type(a).
A.__len__ = __len__.__get__(A)
a.b()
len(a)
5
I am trying to define condition to class, if my object Does not meet the conditions:
The conditions: all the organs is vectors, the shape of the vectors is the same.
When I try to casting the object the function will return None.
Thats what I tried so far:
class Matrix:
def __init__(self,m1):
dic_len = {}
self.m1 = tuple(m1)
checking = 0
for i in self.m1:
dic_len[len(i)] = 'check'
if type(i) != Vector:
self.m1 = None
checking = 1
if len(dic_len) != 1:
self.m1 = None
if len(dic_len) == 1 and checking == 0:
self.content = self.m1 = tuple(m1)
self.shape = (len(m1),len(m1[0]))
def __repr__(self):
if self.m1 != None:
return "(" + ", ".join(str(i) for i in self.m1) + ")"
else:
return None
But I get this error:
>>>v1 = Vector([1,2])
>>>v2 = Vector([4,5,6])
>>>m = Matrix([v1,v2])
>>>print(m)
TypeError: __str__ returned non-string (type NoneType)
i wish the function will return None.
return str(None)
instead of
return None
The CPython docs state for the __repr__ method state that
The return value must be a string object.
So returning None isn't going to work.
>>> class C:
... def __repr__(self):
... return None
...
>>> c = C()
>>> repr(c)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __repr__ returned non-string (type NoneType)
If you're going to share your code with others, it might be better to code __repr__ to produce its coventional output:
...this should look like a valid Python expression that could be used to recreate an object with the same value (given an appropriate environment)...
And override __str__ to produce a representation that indicates the validity of the object instance (though note __str__ must also return a string).
I'm kinda new to python and I can't get past this error:
Traceback (most recent call last):
File "***", line 63, in <module>
bst = Node()
TypeError: __init__() missing 1 required positional argument: 'val'
Basically, the program is a BST which would allow you to insert, search and look for the minimum item by only going left.
Here's the code (sorry, it's hungarian)
class Node:
def __init__(self, val):
self.ertek = val
self.balgyerek = None
self.jobbgyerek = None
self.gyoker = None
def beszur(self, pri):
if self.gyoker:
return self.gyoker.beszur(pri)
else:
self.gyoker = Node(pri)
return True
if self.ertek == pri:
return False
elif self.ertek > pri:
if self.balgyerek:
return self.balgyerek.beszur(pri)
else:
self.balgyerek = Node(pri)
return True
else:
if self.jobbgyerek:
return self.jobbgyerek.beszur(pri)
else:
self.jobbgyerek = Node(pri)
return True
def keres(self, pri):
if self.gyoker:
return self.gyoker.keres(pri)
else:
return False
if(self.ertek == pri):
return True
elif self.ertek > pri:
if self.balgyerek:
return self.balgyerek.keres(pri)
else:
return False
else:
if self.jobbgyerek:
return self.jobbgyerek.keres(pri)
else:
return False
def minimumertek(self):
jelenlegi = self
while(jelenlegi.balgyerek is not None):
jelenlegi = jelenlegi.balgyerek
return self.ertek
bst = Node()
The __init__ method is run as soon as an object of a class is instantiated. Your __init__ method has two positional arguments: self which refers to the object instance and is passed automatically, and val which is assigned to self.ertek. However, you didn't pass the value of val. Hence, the error. Try passing the value of val at the class instantiation. e.g bst = Node('ertek value')
I have a simple class that extends long to accept a string with value modifiers (ie '10m' would be 1024*1024*10)
I have the __str__ function that prints the original value passed in (ie if '10m' is passed in, return '10m')
Problem is that when I call something such as:
>>> printf("%d" % Size('10m'))
I get the following
SystemError: ../Objects/stringobject.c:4044: bad argument to internal function
Obviously if I print "%s" I get '10m'
So the question is, since I'm subclassing long, why does the class call __str__ when it should be getting the long value.
BTW, a bit more testing shows that the %x and %f will print the integer value which confuses me more. I also tried adding the __format__ but that appears to only be called on when "...".format() is called.
EDIT #1, Here's the code:
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)
Not really an answer, but too long for a comment.
I find this question highly interesting. I tried to replicate the behaviour using this:
#! /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)
Behaviour as described by OP. But now comes the funny part: When I inherit from int and not from long, it works smoothly:
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
That is, it works fine in python2, but fails in python3. Strange, strange.
Please see Python issue tracker, 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
This works in 3.4.0alpha4, but not in 3.[0123].