Friends and Opinions
opinion.py: 11 points
In this exercise, you’ll do some computational social science—the code for this exercise models opinions spreading on social networks. Here’s the story you should have in mind: you’ve got a population of people, and each person starts with an initial opinion, which is a number between 0 and 1. Think of 0 as representing “I hate knitting,” and 1 being “I love knitting.” It’s reasonable to expect people to be influenced over time by their friends’ opinions. The code here will be a step towards modeling this phenomenon.
Note: we think of friendship in this lab as one way. Think about this in the same way Instagram friendship works. You do not need to be friends with your roommate on Instagram for them to be friends with you.
We’ll model our simple social network in the following way: each person will be a node, and they’ll have links pointing to the people they like. For example, you might have the following:

Each person’s node is colored in proportional to their initial opinion: 0 is black, 1 is white, and in-between values would be gray. Note that it’s possible to like someone who doesn’t like you. Ang likes Boone, but Boone does not like Ang.
Here’s how we’ll capture opinion dynamics in a step-by-step method. Each step, we’ll pick someone at random from the population. That person will then update their opinion by taking the average opinion of all the people they like, including themselves. In our example, if we chose Ang to update, their opinion would go from 0 to 0.33, because among the people they like, two people hate knitting (themselves and Charlie), and one person loves knitting (Boone). In the updated graph below, Ang’s shade got a bit lighter.

If you update lots of times, you might hope the network will settle on a steady-state opinion, and that this opinion will depend on the network structure. This is in fact what can happen - neat.
The Program
We have started implementing the Person class and some test code in opinion.py – it is set up to be used to do the simulation as described above. Like many files which define and then test/use a class, we have the class definition at the top of the file and the main() function at the bottom. Your goal is to (a) write some of the class methods and (b) test the simulation by adding and running code in main().
Start by taking a look at the Person class’ methods and think through what variables each instance should keep track of. For instance, in befriend(new_friend), we’re told a new person is our friend, and later in update() we’re told to perform an update based on our friends’ opinions. So, an instance of the Person class should probably refer to the same list of friends in both places.
The self keyword
Remember that a class is a general blueprint for creating specific instances of a class. Python uses the generic keyword self to refer to an instance’s specific data and actions. Any data that should not disappear between method calls should be stored in self, and initialized in the __init__ method.
You can always always tell the difference between a method and a generic function by looking at the arguments: a method will always have self as the first argument.
Let’s start with our constructor method.
- In
__init__(self), you should add a line initializingself.friendsas an empty list. Later, any references tofriendsshould reference that attribute. - Other important values to define in the constructor are the Person’s
nameandopinion, represented by a float between 0 and 1, inclusive. Instead of choosing a default name and opinion for every Person, the user will specify the initial values. To facilitate this, the__init__method takes the argumentsnameandopinion. You need to now assign the values of those arguments toself.nameandself.opinion, respectively.
README: Heads Up!
The variable names name, opinion, and friends in the self object must be spelled exactly as shown, or you won’t be able to use your class with the provided simulation code in opinion_helpers.py in Part 2 of the lab.
When making instances of Person, PLEASE DO NOT name two people the same thing, as it can cause you issues down the line. Having the same opinion across people is fine.
Reminder
Be sure to commit and push your changes after you complete the constructor!
Next, we’ll write our other methods.
Go to
befriend(self, new_friend). When this function gets called (for instance, bymain) it makes a connection between two people. You should add the new friend (aPersonobject) to the current instance’s friends list. A reminder that thenameof a person is not the same as aPersonobject! Make sure to befriend an object, not a string.To make things easier later, if we need to befriend multiple people at once, we should support adding a whole list of people (as
Personobjects) to our friends list in one method call. Do this inadd_as_friends(self, friends_to_add). You can use thebefriendmethod you wrote to add one friend to help you here!
Reminder
Now is a good time to commit and push your changes.
All of the methods so far help us make new people and connections in the graph. Once we have all the connections for people in their friends lists, we’ll want to run the simulation. There are two additional methods we’ll need.
The update(self) method will observe the opinions it cares about, which includes the current instance’s friends’ opinions and its own opinions, and update its own opinion to be the average of those. For each instance, its own opinion can be accessed by calling self.opinion, but what about the others?
Luckily, each instance of the Person class stores its own friends list. This means we can actually say self.friends[0].opinion to access the opinion of the first friend in the list.
- Write
update(self)to take the average ofself.opinionand all of the friends’ opinions. Make sure the current instance’s opinion gets updated! You can assume values given will be between 0 and 1.
The last method in the class is the __eq__(self, other). This is already written for you, but you should know that it allows us to check if two Person instances are equal.
Reminder
Commit and push your changes before moving on.
What’s your opinion?
The Person class is fully defined now! The next step is to test it.
Write the
update_step(people_list)function outside thePersonclass. This means it’s below the class, and unindented all the way. You should import therandomlibrary and use it to pick a person frompeople_listand run their update function.Write a
main()function that will help you test theupdate()andupdate_step(people_list)methods. Think about what the simplest example of updating might be.- For
update(), we suggest making twoPersoninstances, one who likes knitting and one who does not, and having one befriend the other. This is like having a network with two people and one arrow connection. If you follow these instructions, once you runupdate(), the person with a friend in their friends list should have an opinion of 0.5. - For
update_step(people_list), we suggest making a population of threePersoninstances and having them befriend a subset of each other. Runupdate_stepfor a substantial number of iterations, then print each person’s opinion to see if/how they changed. Note that because your code uses therandomlibrary, you should get a different output each time! Make sure to run your test code multiple times to make sure you see a variation in input.
- For
Reminder
Don’t forget to commit and push your changes!