I can do this:
class Person:
    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName
    def __str__(self):
        print self.firstName
bob = Person('Robert', 'Smith')
print bob
>>> Robert
But I want to do it more generically rather than overriding __str__ for every class manually.  I've tried using decorators and metaclasses, but I am confounded.  I think that it should be possible to do something similar to this, but I don't know how to make the decorator or metaclass accept an argument and still create a properly instantiable object:
class strAs:
    def __init__(self, prop):
        self.prop = prop
    def __call__(self, cls):
        decoratorSelf = self
        def wrapper(*args, **kwargs):
            def __str__(s):
                return getattr(s, decoratorSelf.prop)
            c = cls(*args, **kwargs)
            c.__str__ = __str__
            return c
        return wrapper
@strAs(prop='firstName')
class Person:
    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName
@strAs(prop='name')
class Dog:
    def __init__(self, name):
        self.name = name
bob = Person('Robert', 'Smith')
fido = Dog('Fido')
print bob
print fido
>>> Robert
Fido
But this fails with:
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/tmp/py6495xVN", line 33, in <module>
    print bob
TypeError: __str__() takes exactly 1 argument (0 given)
I can't figure out why __str__() is not getting self passed to it.  I noticed that the bob instance of the Person class seems to be missing some typical object methods, so I tried I subclassing object like this (and added two print statements):
class strAs(object):
    def __init__(self, prop):
        self.prop = prop
    def __call__(self, cls):
        decoratorSelf = self
        def wrapper(*args, **kwargs):
            print 'in wrapper'
            def __newstr__(s):
                print 'in newstr'
                return getattr(s, decoratorSelf.prop)
            c = cls(*args, **kwargs)
            c.__str__ = __newstr__
            return c
        return wrapper
@strAs(prop='firstName')
class Person(object):
    def __init__(self, firstName, lastName):
        self.firstName = firstName
        self.lastName = lastName
@strAs(prop='name')
class Dog(object):
    def __init__(self, name):
        self.name = name
bob = Person('Robert', 'Smith')
fido = Dog('Fido')
print bob
print fido
And now I get some output with __str__ apparently being called correctly:
>>> in wrapper
in wrapper
<__main__.Person object at 0x7f179389e450>
<__main__.Dog object at 0x7f179389e510>
But it's not printing the property as it should be, and it doesn't appear to even be using the __newstr__ function, because it's not printing in newstr.  However, if I check bob.__str__, I see that it is indeed <function __newstr__ at 0x7f17951d78c0>.
I must be missing something obvious, but I feel like this is over my head right now. :/
