2

I want to create an object that contains some instances of a class. That object should be some kind of list-generalization of the instances. E.g. I have the following class:

class car:
    def __init__(self, prize, color)
        self.prize = prize
        self.color = color

Now I want to have an object cars that contains many instances of the class car but that I can work with like an instance of car, i.e. cars.prize should return a list of the prizes for all the instances I collected in that object cars.

  • `def car():`? not `class car:`? – jizhihaoSAMA Jun 02 '20 at 15:41
  • 1
    Have you tried implementing such a collection? What happened? – jonrsharpe Jun 02 '20 at 15:44
  • I have no idea how to implement this – maximumphysics Jun 02 '20 at 15:49
  • Have you considered making a `cars` list instead? Then you iterate over the list or use direct access to particular elements. The usage looks closer to real world. although you cannot do `cars.prices` to get all the prices without extra work. – progmatico Jun 02 '20 at 15:50
  • That's not really a question. Please note that SO is neither a code-writing nor tutorial service. If you want to know how to implement a container, look at e.g. https://docs.python.org/3/reference/datamodel.html#emulating-container-types. – jonrsharpe Jun 02 '20 at 16:49

4 Answers4

3

I think you can do something like that:

class Cars:
    def __init__(self, list_of_cars):
        self.cars_list = list_of_cars
        self.prize = [car.prize for car in self.cars_list]
        self.color = [car.color for car in self.cars_list]

Let's see how it can be used:

list_of_cars = [Car(1000, "red"), Car(2000, "blue"), Car(3000, "Green")]
x = Cars(list_of_cars)

print(x.prize)
# [1000, 2000, 3000]

print(x.color)
#["red", "blue", "Green"]
Anwarvic
  • 12,156
  • 4
  • 49
  • 69
  • 2
    This means the `prize` and `color` can get out-of-sync with `cars_list`, I'd look at properties or even `__getattr__` to redirect attribute access to the list – jonrsharpe Jun 02 '20 at 15:56
  • How can it get out-of-sync?? Could you give me an example? – Anwarvic Jun 02 '20 at 15:57
  • `x.cars_list.append(Car(4000, "Purple"))`? – jonrsharpe Jun 02 '20 at 15:58
  • That's not how classes work. If you want to add value to the class, you create a method to do so. – Anwarvic Jun 02 '20 at 16:03
  • I don't know what you're trying to say. That *will* work. It violates the Law of Demeter, if that's what you mean? You could implement `append` and other list methods on `Cars` and name the property with the conventional `_` prefix to discourage direct access, but... you haven't. `x.color.append("Purple")` also works, for example. – jonrsharpe Jun 02 '20 at 16:10
  • Not exactly the "LoD"... Look, the basic concept behind encapsulation is to hide the implementation details. So, I can solve this issue by making `cars_list` private. It's simple! Also, I didn't add other methods like `append` or `remove` because that wasn't the question. – Anwarvic Jun 02 '20 at 16:18
  • I am familiar with encapsulation, thank you. And yes there are various solutions (well you can't *really* make it private in Python, but there are conventions), but the point is you *haven't applied any of them* or even mentioned this as a thing the OP should consider. It leaves a big foot-gun with someone who is likely not aware of the implications. More broadly, maybe just don't drop implementation-only answers on low-effort questions. – jonrsharpe Jun 02 '20 at 16:27
  • Actually, I can make it private in python... here `self.__cars_list`. Now, it's private. Secondly, why should I create other methods the OP didn't ask about? Maybe he/she doesn't want it. Anyway, you can tell me what I should do and I will consider it for sure – Anwarvic Jun 02 '20 at 16:37
  • 1
    That's [*name mangled*](https://stackoverflow.com/q/7456807/3001761), but sure. – jonrsharpe Jun 02 '20 at 16:47
  • 1
    I really really enjoyed this discussion. The last link was so informative. I know that was a waste of your time. But definitely it wasn't the same for me. Thanks :) – Anwarvic Jun 02 '20 at 16:52
3

You could create a new class car_list() that holds a list of cars (to which you can append, remove etc). In that class, add aget_prizes() method that returns a list of prizes by iterating over the list. For example:

class car_list():
   def __init__(self, cars):
      self.cars = cars # This is a list

   def get_prizes(self):
      return [car.prize for car in self.cars]
Yaron Grushka
  • 235
  • 2
  • 10
2

Small error in your code: you need a : at the end of your __init__ method definition line:
def __init__(self, prize, color):

Here's an implementation of cars which does what you desire. The use of the @property decorator allows you to access methods as object properties:

class car:
    def __init__(self, prize, color):
        self.prize = prize
        self.color = color

class cars:
    def __init__(self, list_of_cars):
        for one_car in list_of_cars:
            assert isinstance(one_car, car) # ensure you are only given cars
        self.my_cars = list_of_cars

    @property
    def prize(self):
        return [one_car.prize for one_car in self.my_cars]

    @property
    def color(self):
        return [one_car.color for one_car in self.my_cars]


>>> a = car('prize1', 'red')
>>> b = car('prize2', 'green')
>>> c = car('prize3', 'azure')
>>> carz = cars([a,b,c])
>>> carz.prize
['prize1', 'prize2', 'prize3']
>>> carz.color
['red', 'green', 'azure']

You can add more checking on inputs in each object if desired, but this is the basic framework. Hope it helps, Happy Coding!

Sam
  • 1,406
  • 1
  • 8
  • 11
  • So I guess there is no way that the new class `cars` inherits these properties from the class `car` as functions for the list? – maximumphysics Jun 02 '20 at 18:28
  • Not really - it would inherit the initialization of a single prize and a single color from `car`, but that doesn't do what we want. As it stands, `cars` is a pretty thin wrapped for a list of cars, probably only really useful if you want to add more functionality (summing the costs of cars, finding the most popular color, etc) – Sam Jun 02 '20 at 20:40
0

This answer is inspired by the wonderful Sam idea of using decorators. So please give him the point if you think you are pleased with this chunk of code.

def singleton(all_cars):
    instances = {} # cars instances
    def get_instance():
        if all_cars not in instances:
            # all_cars is created once
            instances[all_cars] = all_cars()
        return instances[all_cars]
    return get_instance

@singleton
class all_cars:
    def __init__(self):
        self.inventory = {}

    @property
    def prizes(self):
        return [e.prize for e in self.inventory.values()]
    @property
    def colors(self):
        return [e.color for e in self.inventory.values()]        

class car:
    def __init__(self, prize, color):
        self.prize = prize
        self.color = color

    def register(self):
        # Like class all_cars is a singleton it is instantiate once, reason why dict is saved
        cars = all_cars()
        cars.inventory[len(cars.inventory)] = self


if __name__ == '__main__':
    # Creating cars items
    car1 = car("50", "Blue")
    car2 = car("300", "Red")
    car3 = car("150", "Gray")
    # Register part for cars
    car1.register()
    car2.register()
    car3.register()
    # Like class all_cars is a singleton it is instantiate once, reason why dict is saved
    cars = all_cars()

    print(cars.inventory)
    """{0: <__main__.car object at 0x7f3dbc469400>, ---> This is object car1
    1: <__main__.car object at 0x7f3dbc469518>,  ---> This is object car2
    2: <__main__.car object at 0x7f3dbc469550>}  ---> This is object car3"""
    print(cars.prizes)
    """['50', '300', '150']"""
    print(cars.colors)
    """['Blue', 'Red', 'Gray']"""
Laurent B.
  • 1,653
  • 1
  • 7
  • 16