Access attributes of a list of object using class - python

I want to build a class which is basically an array of objects. I want this class to return me a list of attributes of its members. For example, from the class :
class Animal():
def __init__(self, height, age):
self.height = height
self.age = age
dog = Animal(1,2)
cat = Animal(3,4)
I want to create a class Group which would do:
my_group = Group([dog,cat])
print(my_group.height) #return [1,3] or (1,3) or array([1,3])
I have thought of doing:
import inspect
def get_attribute(instance):
"""Return all attributes of an instance (except all internal built-in)"""
attributes = inspect.getmembers(instance, lambda a:not(inspect.isroutine(a)))
return [a for a in attributes if not(a[0].startswith('__') and a[0].endswith('__'))]
class Group():
def __init__(self, members):
self.members = members
attributes = [a[0] for a in get_attribute(list(members)[0])] #assuming members are all of the same class
for a in attributes:
setattr(self, a, [getattr(m, a) for m in self.members])
I can then use Group for others classes, for example:
class Human():
def __init__(self, name, weight):
self.name = name
self.weight = weight
class Family(Group):
pass
alan = Human("alan", 1)
bob = Human("bob", 2)
my_family = Family([alan, bob])
print(my_family.weight) #returns [1, 2]
This works but seems inefficient if the number of members gets very high since it loops over each members. Basically, my code works but I would like to make it faster using functions like map or something similar.

use a class attribute:
class Animal():
all_data = {"height":[],"age":[]}
def __init__(self, height, age):
self.height = height
self.age = age
Animal.all_data["age"].append(age)
Animal.all_data["height"].append(height)
dog = Animal(1,2)
cat = Animal(3,4)
print(Animal.all_data["height"])
print(Animal.all_data["age"])

Perhaps you can just define a method to keep it dynamic:
class Animal():
def __init__(self, height, age):
self.height = height
self.age = age
dog = Animal(1,2)
cat = Animal(3,4)
class Group():
def __init__(self, members):
self.members = members
def get_all_attr(self, a):
return [getattr(m,a) for m in self.members]
my_group = Group([dog,cat])
print(my_group.get_all_attr("height")) #returns [1,3]
I don't think you can get away without looping over the members every time unless you use lazy initialization.

Related

Old data to new structure

let's say i got some pickled data in an class structure like the following:
class dog:
def __init__(self, nameDog, age):
self.nameDog = namedog
self.age = age
self.favoriteToys = {}
class toy:
def __init__(self, nameToy):
self.nameToy = nameToy
self.color = ''
Now i want to load the data an use it in an new structure with an extra Attribute based on the old structure.
class dog:
def __init__(self, nameDog, age):
self.nameDog = namedog
self.age = age
self.breed = ''
self.favoriteToys = {}
class toy:
def __init__(self, nameToy):
self.nameToy = nameToy
self.color = ''
Is there an easy way to do it?
You might want to create a class that is derived from another. That is called inheritance Python inheritance.
class Rottweiler(dog):
def __init__(self, nameDog, age):
super().__init__(nameDog, age)
self.breed = ''
Using the super() function, your child class (Rottweiler) will automatically inherit the methods and properties from its parent (dog).

Storing list of instances (of self) in class level attribute?

I have a class in which I would like to store a static reference list of objects of the same class. For example:
class Apple:
NICE_APPLES = [Apple('Elstar', 'Green'), Apple('Braeburn', 'Red'),
Apple('Pink Lady', 'Pink')]
def __init__(self, name, colour):
self.name = name
self.colour = colour
This results in a NameError: name 'Apple' is not defined error.
Why doesn't this work?
I've changed the code to the following, which seems to work on the console:
class Apple:
NICE_APPLES = []
def __init__(self, name, colour):
self.name = name
self.colour = colour
Apple.NICE_APPLES = [Apple('Elstar', 'Green'), Apple('Braeburn', 'Red'),
Apple('Pink Lady', 'Pink')]
Is there a better way to do this?
Will this work inside and outside the module, and is this dependent on the way I import the module?
use a classmethod to append apples to your class list.
class Apple:
NICE_APPLES = []
def __init__(self, name, colour):
self.name = name
self.colour = colour
#classmethod
def add_nice_apple(cls, name, colour):
cls.NICE_APPLES.append(cls(name, colour))
Apple.add_nice_apple('Elstar','Green')
Apple.add_nice_apple('Braeburn','Red')
Declare NICE_APPLES as an empty list in the Apple class, and then inside __init__(), when you're done assigning all the local variables, append self to the list.
class Apple(object):
NICE_APPLES = []
def __init__(self, name, color, keep=False):
self.name = name
self.color = color
if keep:
Apple.NICE_APPLES.append(self)
You can create new class instance from class method, like this, I think this is a clean way yo do it, and also if you want to store the recent created obj besides a list of hardcords:
class Apple:
NICE_APPLES = []
def __init__(self, name, colour):
self.name = name
self.colour = colour
#classmethod
def init_with_nice_apples(self, name, colour):
Apple.NICE_APPLES = [Apple('Elstar', 'Green'), Apple('Braeburn', 'Red')] #hardcore list
self.__init__(self,name, colour)
Apple.NICE_APPLES.append(self)
return self
ap = Apple.init_with_nice_apples("Delicius", "Red")

Instance of a modified dict class should return one of the values

I wonder if it is possible to create a Python class with following properties:
It should have all properties of the list or dict, i.e. inherited from one of them
When the instance is called it would return an element predefined by index or key
For example:
class A(dict):
def __init__(self, name):
self.name = name
self['a'] = 1
self['b'] = 2
a = A('a')
and when instance a is called it would return 1 as defined by the key 'a'. The original access through the keys should still be available.
The problem X:
I have a class Fruits that is inherited from dict. In self it can have several instances of class Fruit, however often there is only one instance. So when I am working with instance of Fruits I want to use methods and attributes of the that single instance as if they were attributes and methods of A:
class Fruit(object):
def __init__(self):
self.name = 'apple'
class Fruits(dict):
def __init__(self, name):
self.fruit_name = name
self['apple'] = Fruit()
a = Fruits('apple')
I want a.name to return 'apple'. I have the solution using #property:
class Fruits(dict):
def __init__(self, name):
self.fruit_name = name
self['apple'] = Fruit()
#property
def name(self):
return self[self.fruit_name].name
but I am looking for something better...
Not sure if I've followed your question completely, but you can't call an instance directly; you can call a function within the instance. Now, since a is an instance of dict, you can access its keys -
In [9]: a['a']
Out[9]: 1
Or,
In [10]: a[a.name]
Out[10]: 1
You can implement the __call__ method within A
class A(dict):
def __init__(self, name):
self.name = name
self['a'] = 1
self['b'] = 2
def __call__(self):
return self[self.name]
Then,
In [24]: a = A('a')
In [25]: a()
Out[25]: 1
Is this along the lines of what you're looking for?

Add an object of one class to an object of another class in python

I have a class Participant as follows:
class Participant:
def __init__(self,name,level):
self.name = name
self.level =level
I also have another class Team as follows:
class Team:
def __init__(self,name):
self.name = name
I want to create a method in the class Team to add an instance of class Participant; for instance:
Assume myTeam is an empty, valid instance of Team.
Also assume myParticipant1 is a valid instances of Participant
myTeam.addParticipant(participant= myParticipant1)
AddParticipant method should add the instance myParticipant1 to the instance myTeam.
How do I acheive it in Python?
Aside from the inheritance questions we're talking about in the comments, this is pretty simple stuff.
class Participant(object):
def __init__(self, name, level):
self.name = name
self.level = level
class Team(object):
def __init__(self, name):
self.name = name
self.participants = []
def add_participant(p):
self.participants.append(p)
DEMO:
my_team = Team("Monty Python")
p_info = [("Adam", 10e5), ("Joe-bob", -1)]
participants = [Participant(name, level) for name, level in p_info]
for participant in participants:
my_team.add_participant(participant)
# say that 10 times fast....
In [1]: [p.name for p in my_team.participants]
Out[1]: ["Adam", "Joe-bob"]

Mutually Reference-able Instances in Python

Say I have a pair of instances that reference one another mutually. Is there a preferable manner to structure this relationship than the following.
class Human():
def __init__(self, name):
self.name = name
self.pet = Dog('Sparky', self)
def pet(self, animal):
self.pet.receive_petting()
class Dog(Pet):
def __init__(self, name, owner):
self.name = name
self.owner = owner
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
The thing I don't like is that the relationship needs to be specified in two places. Any ideas on how to make this dryer?
I would break this into three classes:
class Human():
def __init__(self, name):
self.name = name
class Dog(Pet):
def __init__(self, name):
self.name = name
def bark_at(self, person):
"do something"
class OwnerPetRelation():
def __init__(self, dog, human):
self.owner=human
self.pet=dog
Now, one owner can also have many dogs, we just need to define as many OwnerPetRelations.
Similarly, a dog can also belong to multiple owners now.
I would create a method on Human that allows you to add pets (since a human might have many pets):
class Human():
def __init__(self, name):
self.name = name
self.pets = []
def add_pet(self, pet):
pet.owner = self
self.pets.append(pet)
def pet(self, animal):
for pet in self.pets:
pet.receive_petting()
class Dog(Pet):
def __init__(self, name):
self.name = name
self.owner = None
def receive_petting(self):
pass
def bark_at(self, person):
"do something"
This can be used as follows
human = Human('Jim')
human.add_pet(Dog('Woof'))
This approach can of course also be used for just a single pet and one could also extend it to allow pets to be owned by many humans.
There's nothing really Python-specific here; this is just a limitation of constructor-based dependency injection. It's hard to inject a reference to another object that cannot have been created yet. Instead, you can create an object that has a reference to something that will have a reference to the other object. For instance, you can pass a function to the constructor that will be able to return the value:
class Human():
def __init__(self,name,dog):
self.name = name
self._dog = dog
#property
def dog(self):
return self._dog()
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
return self._human()
Then you can use it like this:
human = None
dog = Dog('fido',lambda: human)
human = Human('john',lambda: dog)
print(dog.human.name)
print(human.dog.name)
john
fido
It is not hard to update this so that the property function caches the value, of course. E.g.:
class Dog():
def __init__(self,name,human):
self.name = name
self._human = human
#property
def human(self):
try:
return self._human_
except AttributeError:
self._human_ = self._human()
return self._human_

Categories

Resources