Python: object with a list of objects - create methods based on properties of list members












5















I have a class which contains a list like so:



class Zoo:
def __init__(self):
self._animals =


I populate the list of animals with animal objects that have various properties:



class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length


You can imagine subclasses of Animal that have other properties. I want to be able to write methods that perform the same calculation but on different attributes of the Animal. For example, an average. I could write the following in Zoo:



def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)


That string lookup not only messes with my ability to document nicely, but using getattr seems odd (and maybe I'm just nervous passing strings around?). If this is good standard practice, that's fine. Creating get_average_speed(), get_average_height(), and get_average_length() methods, especially as I add more properties, seems unwise, too.



I realize I am trying to encapsulate a one-liner in this example, but is there a better way to go about creating methods like this based on properties of the objects in the Zoo's list? I've looked a little bit at factory functions, so when I understand them better, I think I could write something like this:



all_properties = ['speed', 'height', 'length']
for p in all_properties:
Zoo.make_average_function(p)


And then any instance of Zoo will have methods called get_average_speed(), get_average_height(), and get_average_length(), ideally with nice docstrings. Taking it one step further, I'd really like the Animal objects themselves to tell my Zoo what properties can be turned into get_average() methods. Going to the very end, let's say I subclass Animal and would like it to indicate it creates a new average method: (the following is pseudo-code, I don't know if decorators can be used like this)



class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length

@Zoo.make_average_function
@property
def tail_length(self):
return self._tail_length


Then, upon adding a Tiger to a Zoo, my method that adds animals to Zoo object would know to create a get_average_tail_length() method for that instance of the Zoo. Instead of having to keep a list of what average methods I need to make, the Animal-type objects indicate what things can be averaged.



Is there a nice way to get this sort of method generation? Or is there another approach besides getattr() to say "do some computation/work on an a particular property of every member in this list"?










share|improve this question

























  • seems pretty complex, but... thing is, as you point out not all animals have same attributes/properties. so the zoo cant just dig in and look at frog tail lenghts to average. as i said, pretty complex, but i’d have animals register their tail length, horn size, etc to a centralized zoo-level dict. attribute name as key, themselves in a list. then the zoo can figure what to do from that registry.

    – JL Peyret
    Nov 21 '18 at 7:28











  • I agree your solution is simpler and safer! I had reached the point of curiosity about how functions truly became methods for objects, and Mad Lee's answer below helped sate me. :)

    – Pebby
    Dec 1 '18 at 0:54
















5















I have a class which contains a list like so:



class Zoo:
def __init__(self):
self._animals =


I populate the list of animals with animal objects that have various properties:



class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length


You can imagine subclasses of Animal that have other properties. I want to be able to write methods that perform the same calculation but on different attributes of the Animal. For example, an average. I could write the following in Zoo:



def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)


That string lookup not only messes with my ability to document nicely, but using getattr seems odd (and maybe I'm just nervous passing strings around?). If this is good standard practice, that's fine. Creating get_average_speed(), get_average_height(), and get_average_length() methods, especially as I add more properties, seems unwise, too.



I realize I am trying to encapsulate a one-liner in this example, but is there a better way to go about creating methods like this based on properties of the objects in the Zoo's list? I've looked a little bit at factory functions, so when I understand them better, I think I could write something like this:



all_properties = ['speed', 'height', 'length']
for p in all_properties:
Zoo.make_average_function(p)


And then any instance of Zoo will have methods called get_average_speed(), get_average_height(), and get_average_length(), ideally with nice docstrings. Taking it one step further, I'd really like the Animal objects themselves to tell my Zoo what properties can be turned into get_average() methods. Going to the very end, let's say I subclass Animal and would like it to indicate it creates a new average method: (the following is pseudo-code, I don't know if decorators can be used like this)



class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length

@Zoo.make_average_function
@property
def tail_length(self):
return self._tail_length


Then, upon adding a Tiger to a Zoo, my method that adds animals to Zoo object would know to create a get_average_tail_length() method for that instance of the Zoo. Instead of having to keep a list of what average methods I need to make, the Animal-type objects indicate what things can be averaged.



Is there a nice way to get this sort of method generation? Or is there another approach besides getattr() to say "do some computation/work on an a particular property of every member in this list"?










share|improve this question

























  • seems pretty complex, but... thing is, as you point out not all animals have same attributes/properties. so the zoo cant just dig in and look at frog tail lenghts to average. as i said, pretty complex, but i’d have animals register their tail length, horn size, etc to a centralized zoo-level dict. attribute name as key, themselves in a list. then the zoo can figure what to do from that registry.

    – JL Peyret
    Nov 21 '18 at 7:28











  • I agree your solution is simpler and safer! I had reached the point of curiosity about how functions truly became methods for objects, and Mad Lee's answer below helped sate me. :)

    – Pebby
    Dec 1 '18 at 0:54














5












5








5


2






I have a class which contains a list like so:



class Zoo:
def __init__(self):
self._animals =


I populate the list of animals with animal objects that have various properties:



class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length


You can imagine subclasses of Animal that have other properties. I want to be able to write methods that perform the same calculation but on different attributes of the Animal. For example, an average. I could write the following in Zoo:



def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)


That string lookup not only messes with my ability to document nicely, but using getattr seems odd (and maybe I'm just nervous passing strings around?). If this is good standard practice, that's fine. Creating get_average_speed(), get_average_height(), and get_average_length() methods, especially as I add more properties, seems unwise, too.



I realize I am trying to encapsulate a one-liner in this example, but is there a better way to go about creating methods like this based on properties of the objects in the Zoo's list? I've looked a little bit at factory functions, so when I understand them better, I think I could write something like this:



all_properties = ['speed', 'height', 'length']
for p in all_properties:
Zoo.make_average_function(p)


And then any instance of Zoo will have methods called get_average_speed(), get_average_height(), and get_average_length(), ideally with nice docstrings. Taking it one step further, I'd really like the Animal objects themselves to tell my Zoo what properties can be turned into get_average() methods. Going to the very end, let's say I subclass Animal and would like it to indicate it creates a new average method: (the following is pseudo-code, I don't know if decorators can be used like this)



class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length

@Zoo.make_average_function
@property
def tail_length(self):
return self._tail_length


Then, upon adding a Tiger to a Zoo, my method that adds animals to Zoo object would know to create a get_average_tail_length() method for that instance of the Zoo. Instead of having to keep a list of what average methods I need to make, the Animal-type objects indicate what things can be averaged.



Is there a nice way to get this sort of method generation? Or is there another approach besides getattr() to say "do some computation/work on an a particular property of every member in this list"?










share|improve this question
















I have a class which contains a list like so:



class Zoo:
def __init__(self):
self._animals =


I populate the list of animals with animal objects that have various properties:



class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length


You can imagine subclasses of Animal that have other properties. I want to be able to write methods that perform the same calculation but on different attributes of the Animal. For example, an average. I could write the following in Zoo:



def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)


That string lookup not only messes with my ability to document nicely, but using getattr seems odd (and maybe I'm just nervous passing strings around?). If this is good standard practice, that's fine. Creating get_average_speed(), get_average_height(), and get_average_length() methods, especially as I add more properties, seems unwise, too.



I realize I am trying to encapsulate a one-liner in this example, but is there a better way to go about creating methods like this based on properties of the objects in the Zoo's list? I've looked a little bit at factory functions, so when I understand them better, I think I could write something like this:



all_properties = ['speed', 'height', 'length']
for p in all_properties:
Zoo.make_average_function(p)


And then any instance of Zoo will have methods called get_average_speed(), get_average_height(), and get_average_length(), ideally with nice docstrings. Taking it one step further, I'd really like the Animal objects themselves to tell my Zoo what properties can be turned into get_average() methods. Going to the very end, let's say I subclass Animal and would like it to indicate it creates a new average method: (the following is pseudo-code, I don't know if decorators can be used like this)



class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length

@Zoo.make_average_function
@property
def tail_length(self):
return self._tail_length


Then, upon adding a Tiger to a Zoo, my method that adds animals to Zoo object would know to create a get_average_tail_length() method for that instance of the Zoo. Instead of having to keep a list of what average methods I need to make, the Animal-type objects indicate what things can be averaged.



Is there a nice way to get this sort of method generation? Or is there another approach besides getattr() to say "do some computation/work on an a particular property of every member in this list"?







python python-3.x list object attributes






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 22 '18 at 2:20









Cheche

816218




816218










asked Nov 21 '18 at 0:37









PebbyPebby

304




304













  • seems pretty complex, but... thing is, as you point out not all animals have same attributes/properties. so the zoo cant just dig in and look at frog tail lenghts to average. as i said, pretty complex, but i’d have animals register their tail length, horn size, etc to a centralized zoo-level dict. attribute name as key, themselves in a list. then the zoo can figure what to do from that registry.

    – JL Peyret
    Nov 21 '18 at 7:28











  • I agree your solution is simpler and safer! I had reached the point of curiosity about how functions truly became methods for objects, and Mad Lee's answer below helped sate me. :)

    – Pebby
    Dec 1 '18 at 0:54



















  • seems pretty complex, but... thing is, as you point out not all animals have same attributes/properties. so the zoo cant just dig in and look at frog tail lenghts to average. as i said, pretty complex, but i’d have animals register their tail length, horn size, etc to a centralized zoo-level dict. attribute name as key, themselves in a list. then the zoo can figure what to do from that registry.

    – JL Peyret
    Nov 21 '18 at 7:28











  • I agree your solution is simpler and safer! I had reached the point of curiosity about how functions truly became methods for objects, and Mad Lee's answer below helped sate me. :)

    – Pebby
    Dec 1 '18 at 0:54

















seems pretty complex, but... thing is, as you point out not all animals have same attributes/properties. so the zoo cant just dig in and look at frog tail lenghts to average. as i said, pretty complex, but i’d have animals register their tail length, horn size, etc to a centralized zoo-level dict. attribute name as key, themselves in a list. then the zoo can figure what to do from that registry.

– JL Peyret
Nov 21 '18 at 7:28





seems pretty complex, but... thing is, as you point out not all animals have same attributes/properties. so the zoo cant just dig in and look at frog tail lenghts to average. as i said, pretty complex, but i’d have animals register their tail length, horn size, etc to a centralized zoo-level dict. attribute name as key, themselves in a list. then the zoo can figure what to do from that registry.

– JL Peyret
Nov 21 '18 at 7:28













I agree your solution is simpler and safer! I had reached the point of curiosity about how functions truly became methods for objects, and Mad Lee's answer below helped sate me. :)

– Pebby
Dec 1 '18 at 0:54





I agree your solution is simpler and safer! I had reached the point of curiosity about how functions truly became methods for objects, and Mad Lee's answer below helped sate me. :)

– Pebby
Dec 1 '18 at 0:54












1 Answer
1






active

oldest

votes


















1














Try this:



import functools
class Zoo:
def __init__(self):
self._animals =

@classmethod
def make_average_function(cls, func):
setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
return func

def get_average(self, propertyname):
return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)


class Animal:
def __init__(self, speed, height, length):
self._speed = speed
self._height = height
self._length = length


class Tiger(Animal):
def __init__(self, tail_length):
self._tail_length = tail_length

@property
@Zoo.make_average_function
def tail_length(self):
return self._tail_length


my_zoo = Zoo()
my_zoo._animals.append(Tiger(10))
my_zoo._animals.append(Tiger(1))
my_zoo._animals.append(Tiger(13))
print(my_zoo.get_average_tail_length())


Note: If there are different zoos have different types of animals, it will lead to confusion.



Example



class Bird(Animal):
def __init__(self, speed):
self._speed = speed

@property
@Zoo.make_average_function
def speed(self):
return self._speed

my_zoo2 = Zoo()
my_zoo2._animals.append(Bird(13))
print(my_zoo2.get_average_speed()) # ok
print(my_zoo.get_average_speed()) # wrong
print(my_zoo2.get_average_tail_length()) # wrong





share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403713%2fpython-object-with-a-list-of-objects-create-methods-based-on-properties-of-li%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    Try this:



    import functools
    class Zoo:
    def __init__(self):
    self._animals =

    @classmethod
    def make_average_function(cls, func):
    setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
    return func

    def get_average(self, propertyname):
    return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)


    class Animal:
    def __init__(self, speed, height, length):
    self._speed = speed
    self._height = height
    self._length = length


    class Tiger(Animal):
    def __init__(self, tail_length):
    self._tail_length = tail_length

    @property
    @Zoo.make_average_function
    def tail_length(self):
    return self._tail_length


    my_zoo = Zoo()
    my_zoo._animals.append(Tiger(10))
    my_zoo._animals.append(Tiger(1))
    my_zoo._animals.append(Tiger(13))
    print(my_zoo.get_average_tail_length())


    Note: If there are different zoos have different types of animals, it will lead to confusion.



    Example



    class Bird(Animal):
    def __init__(self, speed):
    self._speed = speed

    @property
    @Zoo.make_average_function
    def speed(self):
    return self._speed

    my_zoo2 = Zoo()
    my_zoo2._animals.append(Bird(13))
    print(my_zoo2.get_average_speed()) # ok
    print(my_zoo.get_average_speed()) # wrong
    print(my_zoo2.get_average_tail_length()) # wrong





    share|improve this answer




























      1














      Try this:



      import functools
      class Zoo:
      def __init__(self):
      self._animals =

      @classmethod
      def make_average_function(cls, func):
      setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
      return func

      def get_average(self, propertyname):
      return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)


      class Animal:
      def __init__(self, speed, height, length):
      self._speed = speed
      self._height = height
      self._length = length


      class Tiger(Animal):
      def __init__(self, tail_length):
      self._tail_length = tail_length

      @property
      @Zoo.make_average_function
      def tail_length(self):
      return self._tail_length


      my_zoo = Zoo()
      my_zoo._animals.append(Tiger(10))
      my_zoo._animals.append(Tiger(1))
      my_zoo._animals.append(Tiger(13))
      print(my_zoo.get_average_tail_length())


      Note: If there are different zoos have different types of animals, it will lead to confusion.



      Example



      class Bird(Animal):
      def __init__(self, speed):
      self._speed = speed

      @property
      @Zoo.make_average_function
      def speed(self):
      return self._speed

      my_zoo2 = Zoo()
      my_zoo2._animals.append(Bird(13))
      print(my_zoo2.get_average_speed()) # ok
      print(my_zoo.get_average_speed()) # wrong
      print(my_zoo2.get_average_tail_length()) # wrong





      share|improve this answer


























        1












        1








        1







        Try this:



        import functools
        class Zoo:
        def __init__(self):
        self._animals =

        @classmethod
        def make_average_function(cls, func):
        setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
        return func

        def get_average(self, propertyname):
        return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)


        class Animal:
        def __init__(self, speed, height, length):
        self._speed = speed
        self._height = height
        self._length = length


        class Tiger(Animal):
        def __init__(self, tail_length):
        self._tail_length = tail_length

        @property
        @Zoo.make_average_function
        def tail_length(self):
        return self._tail_length


        my_zoo = Zoo()
        my_zoo._animals.append(Tiger(10))
        my_zoo._animals.append(Tiger(1))
        my_zoo._animals.append(Tiger(13))
        print(my_zoo.get_average_tail_length())


        Note: If there are different zoos have different types of animals, it will lead to confusion.



        Example



        class Bird(Animal):
        def __init__(self, speed):
        self._speed = speed

        @property
        @Zoo.make_average_function
        def speed(self):
        return self._speed

        my_zoo2 = Zoo()
        my_zoo2._animals.append(Bird(13))
        print(my_zoo2.get_average_speed()) # ok
        print(my_zoo.get_average_speed()) # wrong
        print(my_zoo2.get_average_tail_length()) # wrong





        share|improve this answer













        Try this:



        import functools
        class Zoo:
        def __init__(self):
        self._animals =

        @classmethod
        def make_average_function(cls, func):
        setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
        return func

        def get_average(self, propertyname):
        return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)


        class Animal:
        def __init__(self, speed, height, length):
        self._speed = speed
        self._height = height
        self._length = length


        class Tiger(Animal):
        def __init__(self, tail_length):
        self._tail_length = tail_length

        @property
        @Zoo.make_average_function
        def tail_length(self):
        return self._tail_length


        my_zoo = Zoo()
        my_zoo._animals.append(Tiger(10))
        my_zoo._animals.append(Tiger(1))
        my_zoo._animals.append(Tiger(13))
        print(my_zoo.get_average_tail_length())


        Note: If there are different zoos have different types of animals, it will lead to confusion.



        Example



        class Bird(Animal):
        def __init__(self, speed):
        self._speed = speed

        @property
        @Zoo.make_average_function
        def speed(self):
        return self._speed

        my_zoo2 = Zoo()
        my_zoo2._animals.append(Bird(13))
        print(my_zoo2.get_average_speed()) # ok
        print(my_zoo.get_average_speed()) # wrong
        print(my_zoo2.get_average_tail_length()) # wrong






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 21 '18 at 6:24









        Mad LeeMad Lee

        537317




        537317






























            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403713%2fpython-object-with-a-list-of-objects-create-methods-based-on-properties-of-li%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

            Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

            A Topological Invariant for $pi_3(U(n))$