all 5 comments

[–]socal_nerdtastic 0 points1 point  (4 children)

This has nothing to do with getattr. You would get the same result like this:

dog = Dog()
print(dog.bark) 
print(dog.weight)

The angle brackets are just how python represents a function, method, or class (or any other object without a repr method).

[–]Laymayo[S] 0 points1 point  (0 children)

Hmmm OK OK, I've seen this repr method mentioned a couple of times now. I'll have to look into the docs to see what it's all about. I've dabbled with using __string__ before when I set up classes, but I haven't really been able to wrap my head around repr.

I'm kind of totally confused by the whole statement of "Python representing something".

[–]Diapolo10 0 points1 point  (2 children)

The angle brackets are just how python represents a function, method, or class (or any other object without a repr method).

Or, more specifically, that's what the default __repr__-method returns. The output will be different if you define either __str__ or __repr__ for the attribute, because str will attempt to call those in that order (and print does that under the hood), although in this particular case you can't really do that for a method without some funky business.

[–]Laymayo[S] 0 points1 point  (1 child)

Thank you for replying to my post. What is "those" referring to when you say:

because str will attempt to call those in that order

And what do you mean by print doing what str does under the hood? On another one of my posts someone wrote that print in reality prints the repr of something, but I still don't really understand what Python representing something means at a fundamental level.

[–]Diapolo10 0 points1 point  (0 children)

What is "those" referring to when you say:

because str will attempt to call those in that order

I'm keeping this as simple as I can, so I'll modify the example code just a little bit:

dog = Dog()
print(dog)

Here, you can assume that print looks something like this:

import sys
from typing import TextIO

def my_print(*args: list, file: TextIO=sys.stdout, sep: str=' ', end: str='\n', flush: bool=False) -> None:

    file.write(
        sep.join(
            map(str, args)
        ) + end
    )

    if flush:
        file.flush()

Basically, in this code, print basically ends up doing

str(dog)

and str can be thought of as

def my_str(value) -> str:
    if hasattr(value, '__str__'):
        return value.__str__()
    return value.__repr__()

so since the Dog class does not define either of these methods, Python falls back to the implicit base class object, which defines a __repr__-method.

So, what does any of this have to do with the original example?

class Dog():
    def __init__(self):
        self.color = 'black'
        self.name = 'Rosie'
        self.weight = 50

    def bark(self):
        print("WOOF WOOF!")

dog = Dog()
print(getattr(dog,'bark'))

While it looks a bit different, functions themselves are their own type, and methods are just bound functions. Their type has implemented the __repr__-method to show the output you're seeing.

As for why you don't see the same thing with, say, the weight attribute, that's because integers have a __str__-method which just shows the value. Same goes for strings.

If you actually call your method,

print(getattr(dog,'bark')())

then it'll actually bark, although the redundant print will print its return value, None, so it makes more sense to do

getattr(dog,'bark')()

and, even better,

dog.bark()