0

I have following code, Dog and Cat class is inherited from Animal class. and Person class is using Dog and Cat class as its instance. I would like to have function to return instance's name like string "get the name xxx" for john instance and john's property dog, cat's name. I have tried to wriiten the property namegetter under Animal class and Person and I can get what I want. however, if there is even a proper way to do that? so if I have couple of class like Person, I do not need to write the same property under the class

class Animal(object):
    def __init__(self, name):
        self.name = name

    @property
    def namegetter(self):
        return 'get the name of {}'.format(self.name)

class Cat(Animal):
    def __init__(self, food, name):
        super().__init__(name)
        self.food = food
        self.sound = 'meow'

class Dog(Animal):
    def __init__(self, food, name):
        super().__init__(name)
        self.food = food
        self.space = 'park'


class Person(object):

    def __init__(self, name):
        self.name = name
        self.dog = Dog("dog's food", "dog's name" )
        self.cat = Cat("dog's food", "cat's name")

    @property
    def namegetter(self):
        return 'get the name of {}'.format(self.name)

if __name__ == '__main__':
    john = Person('john')
    print(john.name) #john
    print(john.dog.name) #dog's name
    print(john.cat.name) #cat's name
    print(john.dog.namegetter) #get the name of dog
    print(john.cat.namegetter) #get the name of cat
    print(john.namegetter) #get the name of john
jacobcan118
  • 7,797
  • 12
  • 50
  • 95
  • 2
    Are you asking if there's another way you can get the instance's `name` out that doesn't use `@property`? I don't really understand your question. – MooingRawr Nov 08 '17 at 16:08
  • not really, I want to know if there;s another way to get instance's name with "get the name of " from john, john.dog, john.cat without using @property or do not write the same property twice – jacobcan118 Nov 08 '17 at 16:09
  • They aren't the same property (even though the implementations look the same), because they act on unrelated objects. Perhaps you want a common base class like `NamedEntity` (that `Animal` and `Person` both inherit from) that defines the `name` property. – chepner Nov 08 '17 at 16:17
  • you can implement `__str__` for your `Animal` object to return name. Then you can use it like: `print john.dog`. But in general what's you have implemented is ok. Probably you can make a class like `Nameble` or whatever where you have constructor with name + getter. Your objects just should extend from it. – iurii_n Nov 08 '17 at 16:17
  • I gave you an example below for Either an additional base class or a Mixin (use the base class here, the Mixin helps with more complicated structures). Your code is quite okay, though. – ingofreyer Nov 08 '17 at 16:23
  • If my answer was helpful to you, I would be very happy, if you could mark it as the accepted answer. – ingofreyer Nov 13 '17 at 12:57

1 Answers1

1

Based on the comments, I guess you want to avoid defining the same method twice. You could do that with Mixins (in a complex hierarchy) or just another base class. As for your question on whether or not you can avoid writing @property: You can leave it out, but then you would have to write braces behind your method namegetter().

Example using a base class:

class NamedEntity(object):
    def __init__(self, name):
        self.name = name

    @property
    def namegetter(self):
        return 'get the name of {}'.format(self.name)


class Animal(NamedEntity):
    pass


class Cat(Animal):
    def __init__(self, food, name):
        super(Cat, self).__init__(name)
        self.food = food
        self.sound = 'meow'


class Dog(Animal):
    def __init__(self, food, name):
        super(Dog, self).__init__(name)
        self.food = food
        self.space = 'park'


class Person(NamedEntity):

    def __init__(self, name):
        super(Person, self).__init__(name)
        self.dog = Dog("dog's food", "dog's name" )
        self.cat = Cat("dog's food", "cat's name")


if __name__ == '__main__':
    john = Person('john')
    print(john.name) #john
    print(john.dog.name) #dog's name
    print(john.cat.name) #cat's name
    print(john.dog.namegetter) #get the name of dog
    print(john.cat.namegetter) #get the name of cat
    print(john.namegetter) #get the name of john

In a more complex example, you could use a Mixin instead, this would basically change the way you inherit from it in the class definition:

class NamedEntityMixin(object):
    def __init__(self, name):
        self.name = name

    @property
    def namegetter(self):
        return 'get the name of {}'.format(self.name)


class Animal(NamedEntityMixin, object):
    # Imagine object being another parent class with additional properties and methods
    pass


class Cat(Animal):
    def __init__(self, food, name):
        super(Cat, self).__init__(name)
        self.food = food
        self.sound = 'meow'


class Dog(Animal):
    def __init__(self, food, name):
        super(Dog, self).__init__(name)
        self.food = food
        self.space = 'park'


class Person(NamedEntityMixin, object):
    # Imagine object being another parent class with additional properties and methods
    def __init__(self, name):
        super(Person, self).__init__(name)
        self.dog = Dog("dog's food", "dog's name" )
        self.cat = Cat("dog's food", "cat's name")


if __name__ == '__main__':
    john = Person('john')
    print(john.name) #john
    print(john.dog.name) #dog's name
    print(john.cat.name) #cat's name
    print(john.dog.namegetter) #get the name of dog
    print(john.cat.namegetter) #get the name of cat
    print(john.namegetter) #get the name of john

Setters

In the comments you asked about what if someone wants to write to namegetter. Above, I only defined readonly access, since the name namegetter sounded to me as if you need that. Below, I defined you a version with getters and setters that allow you to set the format string with the optional variable {name} being replaced on demand:

class NamedEntity(object):
    def __init__(self, name):
        self.name = name
        self._namegetter = "get the name of {name}"

def _get_namegetter(self):
    return self._namegetter.format(name=self.name)

def _set_namegetter(self, namegetter):
    self._namegetter = namegetter

namegetter = property(_get_namegetter, _set_namegetter)


class Animal(NamedEntity):
    pass


class Cat(Animal):
    def __init__(self, food, name):
        super(Cat, self).__init__(name)
        self.food = food
        self.sound = 'meow'
        self.namegetter = 'catnamegetter'


class Dog(Animal):
    def __init__(self, food, name):
        super(Dog, self).__init__(name)
        self.food = food
        self.space = 'park'


class Person(NamedEntity):

    def __init__(self, name):
        super(Person, self).__init__(name)
        self.dog = Dog("dog's food", "dog's name" )
        self.cat = Cat("dog's food", "cat's name")


if __name__ == '__main__':
    john = Person('john')
    print(john.name) #john
    print(john.dog.name) #dog's name
    print(john.cat.name) #cat's name
    print(john.dog.namegetter) #get the name of dog
    print(john.cat.namegetter) #get the name of cat
    print(john.namegetter) #get the name of john

I split up the getter & setter into two methods and made them available as the namegetter property. Python will select the right method based on how you access the property automatically.

The above solution however does not use decorators anymore, which might make the code less readable. A version with decorators is this one. Please note that the method namegetter is defined twice with different decorators:

class NamedEntity(object):
    def __init__(self, name):
        self.name = name
        self._namegetter = "get the name of {name}"

    @property
    def namegetter(self):
        return self._namegetter.format(name=self.name)

    @namegetter.setter
    def namegetter(self, namegetter):
        self._namegetter = namegetter



class Animal(NamedEntity):
    pass


class Cat(Animal):
    def __init__(self, food, name):
        super(Cat, self).__init__(name)
        self.food = food
        self.sound = 'meow'
        self.namegetter = 'catnamegetter'


class Dog(Animal):
    def __init__(self, food, name):
        super(Dog, self).__init__(name)
        self.food = food
        self.space = 'park'


class Person(NamedEntity):

    def __init__(self, name):
        super(Person, self).__init__(name)
        self.dog = Dog("dog's food", "dog's name" )
        self.cat = Cat("dog's food", "cat's name")


if __name__ == '__main__':
    john = Person('john')
    print(john.name) #john
    print(john.dog.name) #dog's name
    print(john.cat.name) #cat's name
    print(john.dog.namegetter) #get the name of dog
    print(john.cat.namegetter) #get the name of cat
    print(john.namegetter) #get the name of john
ingofreyer
  • 1,086
  • 15
  • 27
  • why i cannot put ```super().__init__(name)``` to use it? – jacobcan118 Nov 08 '17 at 17:47
  • and what if I have instance that is named exactly like * namegetter*. does that mean, some conflict occurs? – jacobcan118 Nov 08 '17 at 17:54
  • `super().__init__(name)` would work in Python 3, but you flagged your question for both Python 2.7 and Python 3, so I gave you the Python 2.7 example. See https://stackoverflow.com/a/576183/1331407 for details. – ingofreyer Nov 09 '17 at 07:31
  • I do not undrstand what exactly you mean by an instance named `namegetter`. As a value for the `name` property that is completely fine. You cannot save it as an additional attribute to `Person` though. – ingofreyer Nov 09 '17 at 07:34
  • thank you i mean what if someone inherited Animal class like class Cat(Animal): def __init__(self, food, n): super().__init__(name=n) self.food = food self.sound = 'meow' self.namegetter = 'namegetter' – jacobcan118 Nov 09 '17 at 23:05
  • Right now, that would result in an AttributeError, since `@property namegetter` has been defined readonly. You could, however, add a setter in order to set whatever value you want in there. I will extend my answer accordingly. – ingofreyer Nov 10 '17 at 10:23