0

I am right now studying Python classes and running into a thing which book doesn't explain much.

I have a code :

class Human(object):
    def __init__(self,*args,**kwargs):
        self.first_name = kwargs.setdefault('first')
        self.last_name = kwargs.setdefault('last')
        self.height = kwargs.setdefault('height')
        self.weight = kwargs.setdefault('weight')

def bmi(self):
    return self.weight/float(self.height)**2
Human.bmi = bmi

def human_str(self):
    return '{} {}'.format(self.first_name, self.last_name)
Human.__str__ = human_str

me = Human(first='Seth', last='Gibson')   
adam_mechtley = Human(first='Adam', last='Mechtley', weight = 78, height = 1.85) 
sis = Human(first='Ruth',last='Gibson')
babysis = Human(first='Emily',last='Gibson')
print(sis)
#Prints : proper name
print(sis,babysis)
#Prints : <place in memory>

I understand overall what's happening , but the question is, why when I print only one instance, str() attribute works, but if I try to print two instances at the same time, repr() attribute is being used?

Thanks for your time!

Vlad
  • 387
  • 3
  • 17
  • 2
    That sure is a backwards way of defining a class – bphi Jun 13 '18 at 14:34
  • 1
    If this was Python 3, I'd say that `print(x)` uses `__str__`, while `print((x,y))` calls `__str__` of `tuple`, which internally calls `__repr__` of the elements, but it does not print the `(...)` in Python 2, i.e. not a tuple. Might still be something like that. – tobias_k Jun 13 '18 at 14:37
  • 1
    Cannot reproduce. When I run this code in Python 2.7.10, I get `Ruth GibsonRuth Gibson Emily Gibson` as the output. – jwodder Jun 13 '18 at 14:40
  • Actually, on closer inspection, I can not reproduce the problem either, using `Python 2.7.15rc1`, neither with this "backwards" way, nor with proper `def __str__(self)`, nor with both `str` and `repr`. – tobias_k Jun 13 '18 at 14:43
  • Sorry, guys did a small mistake instead of 'print sis' and 'print sis,babysis', should be 'print(sis)' and 'print(sis,babysis)'. Sorry for confusion, parentheses was missing. Then my question will make sense. – Vlad Jun 13 '18 at 14:53

2 Answers2

2

The reason is that in Python 2, print(a,b) does not print a and b, but it prints (a, b), i.e. a tuple. Thus, print(a) will call str(a) (the (...) won't make a single element into a tuple), whereas print(a,b) calls the __str__ of the tuple (a, b), which in turn calls the __repr__ of the individual elements. (And if no __repr__ is defined, then it will print the type and memory address by default.)

Minimal example:

class Test:
    def __str__(self):
        return "str"
    def __repr__(self):
        return "repr"

t = Test()
print(t)
print(t, t)

Output using Python 2:

str
(repr, repr)

On Python 3, on the other hand, or when doing print t, t in Python 2, without the (...), print will call str on both elements directly.

str
str str

Also, unrelated to the problem, I'd suggest changing your Human class to something like this, defining proper methods and not using kwargs.

class Human(object):
    def __init__(self, first, last, height=None, weight=None):
        self.first_name = first
        self.last_name = last
        self.height = height
        self.weight = weight

    def bmi(self):
        return self.weight/float(self.height)**2

    def __str__(self):
        return '{} {}'.format(self.first_name, self.last_name)
tobias_k
  • 81,265
  • 12
  • 120
  • 179
1

Here's a more normal way to define the class that you wrote earlier

class Human(object): 
    def __init__(self,*args,**kwargs):
        self.first_name = kwargs.setdefault('first')
        self.last_name = kwargs.setdefault('last')
        self.height = kwargs.setdefault('height')
        self.weight = kwargs.setdefault('weight') 
    def bmi(self):
        return self.weight/float(self.height)**2
    def __str__(self):
        return '{0} {1}'.format(self.first_name, self.last_name)

The rest of your question is probably answered here: Difference between __str__ and __repr__?

David Culbreth
  • 2,610
  • 16
  • 26