how to "broadcast" a message to all objects of a class? - python

class Person:
def __init__(self, name):
self.name = name
self.bucket = []
def announce(self, *args):
# ???
pass
def put(self, item):
self.bucket.append(item)
p1 = Person("ya")
p2 = Person("yu")
p1.put("apple")
now I want to somehow announce to all Person() objects that I have an apple in my bucket, they should put one apple in their bucket too if they want.

Simple implementation could be:
class Person:
persons = set()
def __init__(self, name):
self.name = name
self.bucket = []
self.persons.add(self)
def announce(self, msg):
print("[{}]: {}".format(self.name,msg))
#classmethod
def broadcast(cls, person, msg):
for p in cls.persons:
if not p is person:
p.announce(msg)
def put(self, item):
self.bucket.append(item)
self.broadcast(self, '{}: got {} in my bucket!'.format(self.name, item))
p1 = Person("ya")
p2 = Person("yu")
p1.put("apple")
Person.broadcast(None, "Hey! Everybody can broadcast message!")
Output:
[yu]: "ya: got apple in my bucket!
[ya]: Hey! Everybody can broadcast message!
[yu]: Hey! Everybody can broadcast message!
That implementation lacks in
No deregister implementation
No thread save
Just Person and it's subclass can be informed
It is just a toy, you need to adapt it to your real case
Maybe is better you implement an Observer pattern better than that simple one.

Implementing the observer-pattern in python is quite simple,
The basic idea "I want Object A, object B, Object C to get notifications from a specified messaging Object". Therefore you somehow have to connect them, in observer Pattern this process is called "subscription". So your Object A, B, C (Observers) are subscribing to the Object that delivers messages (Subject)
This example is a basic implementation. I didn't addapt it to your code, but alice and bob would be Persons in your case.
class Mailbox :
def __init__(self, ownersName):
self.owner = ownersName
self.messages = []
self.newMessageObservers = []
def deliverMessage(self, message):
self.messages.append(message)
for notifyNewMessage in self.newMessageObservers:
notifyNewMessage(message, self.owner)
def subscribe(self, observer):
self.newMessageObservers.append(observer)
class MailboxObserver :
def __init__(self, observerName):
self.name = observerName
def newMessageHandler(self, contents, owner):
print self.name + " observed a new message in " +\
owner + "'s mailbox"
print "The message said: " + contents
# create the observers
alice = MailboxObserver("alice")
bob = MailboxObserver("bob")
# create a mailbox
alicesMailbox = Mailbox("alice")
# have bob and alice subscribe to alice's mailbox
# registering their 'newMessageHandler' method
alicesMailbox.subscribe(bob.newMessageHandler)
alicesMailbox.subscribe(alice.newMessageHandler)
# put a new message into alice's mailbox
alicesMailbox.deliverMessage("Hello, world!")
source: http://www.philipuren.com/serendipity/index.php?/archives/4-Simple-Observer-Pattern-in-Python.html

Just keep a normal variable in your class (Not a member), and update it whenever you want to "announce" something to all classes.
class Person:
bApple = False
def __init__(self, name):
self.name = name
self.bucket = []
def announce(self, *args):
# ???
pass
def put(self, item):
self.bucket.append(item)
def hasApple(self):
if Person.bApple:
return "True"
else:
return "False"
p1 = Person("ya")
p2 = Person("yu")
p1.put("apple")
print "p1 has Apple? " + p1.hasApple()
print "p2 has Apple? " + p2.hasApple()
Person.bApple = True
print "p1 has Apple? " + p1.hasApple()
print "p2 has Apple? " + p2.hasApple()

Related

yaml.constructor.ConstructorError: while constructing a Python object cannot find "module_name" in the module '__main__'

I have tried construct some classes from file.yml
Stucture of priters.yml you can see bellow:
--- !Station
recipients:
- &first_phone ['Max']
- &second_phone ['Anna', 'Alisa']
obj:
- &first !!python/object:__main__.Nokia
model: Nokia_8800
recipients: *first_phone
- &second !!python/object:__main__.Apple
model: iPhone4
recipients: *second_phone
spam_station: !station
Nokia: *first
Apple: *second
The class constructor is presented in spam_station.py
from abc import ABC, abstractmethod
from typing import Union
from yaml import YAMLObject, load
class AbstrackPhone(ABC):
#abstractmethod
def send_message(self, message):
pass
class Nokia(AbstrackPhone):
def __init__(self):
self.model = None
self.recipients = []
def send_message(self, message):
for recipient in self.recipients:
print(f"Hi {recipient} i'm {self.model}. {message}")
class Apple(AbstrackPhone):
def __init__(self):
self.model = None
self.recipients = []
def send_message(self, message):
for recipient in self.recipients:
print(f"Hi {recipient} i'm {self.model}. {message}")
class ConstructStation(YAMLObject):
yaml_tag = u'!Station'
#classmethod
def from_yaml(Class, loader, node):
def get_satation(loader, node):
data = loader.construct_mapping(node)
station = Class.Station()
station.add_phones(data.values())
return station
loader.add_constructor(u"!station", get_satation)
return loader.construct_mapping(node)['spam_station']
class Station():
def __init__(self):
self.senders = []
def add_phones(self, phones: Union[list, str]):
self.senders.extend(phones)
def send_message(self, message, **kwargs):
for sender in self.senders:
sender.send_message(message, **kwargs)
def station():
with open('../yaml_config/printers') as file:
spam_station = load(file)
return spam_station
if __name__ == "__main__":
station().send_message('Good luck!!!')
I've tried import and use 'station' in sender.py:
from station.spam_station import station
if __name__ == "__main__":
station().send_message('Good luck!!!')
when i run spam_station.py, it's ok:
Hi Max i'm Nokia_8800. Good luck!!!
Hi Anna i'm iPhone4. Good luck!!!
Hi Alisa i'm iPhone4. Good luck!!!
when i run sender.py, i've error:
yaml.constructor.ConstructorError: while constructing a Python object
cannot find 'Nokia' in the module '__main__'
in "../yaml_config/printers", line 7, column 11
How to solve this problem? Please tell me, what is a good practice for configuring python objects to yaml. Thanks!
The problem is that when you execute sender.py, that file (and not spam_station.py) is __main__.
Probably the best solution is to avoid depending on import paths in the YAML file. You already do that with Station, so you can simply do it on the other classes as well:
class Nokia(AbstrackPhone, YAMLObject):
yaml_tag = u"!Nokia"
def __init__(self, model = None, recipients = []):
self.model = model
self.recipients = recipients
def send_message(self, message):
for recipient in self.recipients:
print(f"Hi {recipient} i'm {self.model}. {message}")
Now you can use !Nokia instead of !!python/object:__main__.Nokia in the YAML file. Same goes for the Apple class.

Searching for an ID in a list of books

I've made a simple library system which stores books with an ID, name and cost. My question is rather simple but my limited knowledge of python has let me down.
I've created a class that stores books in the library, they are created like this;
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
I have to make a function that searches for a book by its ID, by making a function in my library class. I tried to make it like in the code below, but it didn't work. basically, I want to give my function an ID, and it should return the name and cost of that particular book, but only if the ID is present in the list.
class Book:
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
class Library:
def __init__(self):
self.books = []
def add_book(self, Book):
self.books.append(Book)
def remove_book(self, Book):
self.books.remove(Book)
#def show_id(self, ID):
# if ID in lib:
# return self.books
def show_all(self):
for Book in self.books:
Book.show()
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
#1.show_id
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.remove_book(book2)
lib.show_all()
I think the simplest idea if you need ID indexing is to use a dictionary:
class Library:
def __init__(self):
self.books = dict()
def add_book(self, Book):
self.books[Book.ID] = Book
def remove_book(self, Book):
del self.books[Book.ID]
def get_book(self, ID):
return self.books[ID]
def show_id(self, ID):
self.get_book(ID).show()
def show_all(self):
for Book in self.books.values():
Book.show()
You could even rename get_book to __getitem__, this second name is special in python, it's called a dunder method (or magic method). Implementing it will allow you to write lib[id] instead of lib.show_id(id) (I'm not saying that you should, but that's an option). There are many other dunder methods that you can try using for fun, you can find some of them in the python data model.
I think that you should post your code on codereview as you may use broader advices on your code.
As noted above, there might be better implementations, but using your current code, you can do the below where I have adapted your show_id function according to requirements. It relies on a list comprehension to identify the correct IDs.
Hope this helps!
class Book:
def __init__(self, ID, name, price):
self.ID = ID
self.name = name
self.price = price
def show(self):
print(self.ID, self.name, self.price)
def get_attribute_string(self):
print(str(self.ID) + '_' + str(self.name) + '_' + str(self.price))
def get_id(self):
print(self.ID)
def get_name(self):
print(self.name)
def get_price(self):
print(self.price)
class Library:
def __init__(self):
self.books = []
def add_book(self, Book):
self.books.append(Book)
def remove_book(self, Book):
self.books.remove(Book)
def show_id(self, ID):
# simple list comprehension to filter; note that you might need to make sure that e.g. all IDs are int, or str
matching_ids = [book for book in self.books if book.ID == ID]
# return name, price tuple for all matching books as requested -- note that this will return a list of all matches
# if the IDs are truly unique, you can return e.g. a single tuple or Book object here
# if nothing found, will return empty list
return [(book.name, book.price) for book in matching_ids]
def show_all(self):
for Book in self.books:
Book.show()
if __name__ == '__main__':
lib = Library()
book1 = Book(1, 'Bookname1', "$30")
book2 = Book(2, 'Bookname2', "$10")
book3 = Book(3, 'Bookname3', "$40")
#1.show_id
lib.add_book(book1)
lib.add_book(book2)
lib.add_book(book3)
lib.remove_book(book2)
lib.show_all()
print(lib.show_id(1))
Even if this method is not really optimized, you can go through them with a for loop.
for book in lib.books:
if book.id == searched_id:
searched_book = book
break

Saving from dictionary to file (error)

I'm taking my first Python class so please bear with me, I have ZERO experience in programming but I'm very eager to learn. If you could steer me in the right direction I'd really appreciate it. Thank you in advance.
I've looked through previous questions but I wasn't able to find one that fully helped/explained where I'm getting stuck. I have a dictionary that stores team members(names, phone, jersey) and need to be able to write this to a file. Below is what I currently have, when I run this I get the error AttributeError:'dict' object has no attribute 'getname'.
class Member:
def get name(self):
return self.name
def get phone(self):
return self.phone
def get jersey(self):
return self.jersey
members={}
def saveData(members, filename):
filename=input("Filename to save:")
outFile=open(filename,"wt")
for x in members.keys():
name=members[x].getname
phone=members[x].getphone
jersey=members[x].getjersey
outFile.write(name+","+phone","+jersey+"\n")
print("Data Saved")
outFile.close()
Member class
You've put a space in the function name, so it won't work.
Too, you don't seem to have an __init__ function.
class Member:
def __init__(self, name, phone, jersey):
self.name = name
self.phone = phone
self.jersey = jersey
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def get_jersey(self):
return self.jersey
Anyway, it's a lot easier that just don't make these get functions; the user can get the variables of a class using the dot syntax.
class Member:
def __init__(self, name, phone, jersey):
self.name = name
self.phone = phone
self.jersey = jersey
shell:
>>> member1 = Member("Dave", "123456789", "red")
>>> member.name
'Dave'
>>> member.phone
'123456789'
>>> member.jersey
'red'
saveData function
It won't work, you should do this:
def saveData(members): # don't include filename, it's going to be redefined later
filename = input("Filename to save: ") # space at the end
with open(filename, 'wt') as outFile: # using with for files is recommended
# then you don't need to close the file
for x in members: # you can directly iterate from a dict
name = x.get_name() # you didn't call the function at all
phone = x.get_phone() # members[x] not necessary
jersey = x.get_jersey()
outFile.write(name+", "+phone+", "+jersey+"\n") #missing + sign
print("Data Saved")
Working example
__init__.py
class Member:
def __init__(self, name, phone, jersey):
self.name = name
self.phone = phone
self.jersey = jersey
def get_name(self):
return self.name
def get_phone(self):
return self.phone
def get_jersey(self):
return self.jersey
def saveData(members):
filename = input("Filename to save: ")
with open(filename, 'wt') as outFile:
for x in members:
name = x.get_name()
phone = x.get_phone()
jersey = x.get_jersey()
outFile.write(name+", "+phone+", "+jersey+"\n")
print("Data Saved")
IDLE shell
>>> members = [Member("Dave", "123456789", "red"),
Member("Tom", "133742097", "yellow"),
Member("Elisa", "122333444", "blue"),
Member("John", "987654321", "blue")
]
>>> saveData(members)
Filename to save: output.txt
Data Saved
output.txt
Dave, 123456789, red
Tom, 133742097, yellow
Elisa, 122333444, blue
John, 987654321, blue
You can define getname, getphone in your Member class as follows:
class Member:
def getname(self):
return self.name
def getphone(self):
return self.phone
def getjersey(self):
return self.jersey
Then you can get values from getters in your saveData function :
name=members[x].getname()
phone=members[x].getphone()
jersey=members[x].getjersey()

Python SMS store program using class and methods - has_been_viewed status

from datetime import datetime
class sms_store:
store = []
read = []
def add_new_arrival(self,number,time,text):
sms_store.read.append(len(sms_store.store))
sms_store.store.append(("From: {}, Recieved: {}, Msg: {}".format(number,time,text)))
def delete(self,i):
try:
del sms_store.store[i]
except IndexError:
print("Index is out of range. Cannot delete")
def message_count(self):
return print("Amt of messages in inbox: {}".format(len(sms_store.store)))
def viewall(self):
print(sms_store.store)
def get_unread_indexes(self):
#### ###################################I need help for this method.
def get_message(self,i)
print(sms_store.store[i])
### tests ####
time = datetime.now().strftime('%H:%M:%S')
my_inbox = sms_store() #instantiate an object 'store' for class
my_inbox.add_new_arrival("12345",time,"Hello how are you?") #instance of store object
my_inbox.add_new_arrival("1111111",time,"BYE BYE BYE")
my_inbox.viewall()
my_inbox.msgcount()
Thanks for viewing this.
This is what I need to do:
my_inbox.add_new_arrival()
When adding a new message, its has_been_viewed status is set False.
my_inbox.get_unread_indexes()
Returns list of indexes of all not-yet-viewed SMS messages
my_inbox.get_message(i)**
Return (from_number, time_arrived, text_of_sms) for message[i]
Also change its state to "has been viewed".
If there is no message at position i, return None
Please help me on those above methods!?
Thank you so much!
Hi I tweaked your code a bit, I think I have done this before in the "How to think like a computer Scientist Book", Hope it works for you.
from datetime import datetime
and
class SMS_store:
then
def __init__(self):
self.store = []
def __str__(self):
return ("{0}".format(self))
def add_new_arrival(self, number, time, text ):
self.store.append(("Read: False", "From: "+number, "Recieved: "+time, "Msg: "+text))
def message_count(self):
return (len(self.store))
def get_unread_indexes(self):
result = []
for (i, v) in enumerate(self.store):
if v[0] == "Read: False":
result.append(i)
return (result)
def get_message(self, i):
msg = self.store[i]
msg = ("Read: True",) + msg[1:]
self.store[i] = (msg)
return (self.store[i][1:])
def delete(self, i):
del self.store[i]
def clear(self):
self.store = []
Why don't you add another list to your class called unread. Change add_new_arrival to add the message to unread.
Then under the get_message method move the specified message from unread to read.
Lastly your get_unread method just lists the indexes of the unread list.
Python SMS store program using class and methods - has_been_viewed status
import time
class SMS_store:
def __init__(self):
self.inbox = []
def add_new_arrival(self, from_number, text_of_sms,read_status = False):
number = str(from_number)
time_received = time.strftime("%D %T")
self.inbox.append([time_received, number, text_of_sms, read_status])
def message_count(self):
return "There are {0} messages in your Inbox".format(len(self.inbox))
def get_unread_indexes(self):
unread = []
for index, message in enumerate(self.inbox):
if False in message:
unread.append(index)
return "Unread Messages in:", unread
def get_message(self, index):
message = self.inbox[index]
message[3] = "Read"
return message[ : 3]
def delete(self, index):
del self.inbox[index]
return "Deleted Message", index
def clear(self):
self.inbox = []
return "Empty Inbox"

How can I use a returned value to open up another instance of a class in Python?

I am on exercise 43 doing some self-directed work in Learn Python The Hard Way. And I have designed the framework of a game spread out over two python files. The point of the exercise is that each "room" in the game has a different class. I have tried a number of things, but I cannot figure out how to use the returned value from their initial choice to advance the user to the proper "room", which is contained within a class. Any hints or help would be greatly appreciated.
Apologies for the poor code, I'm just starting out in python, but at my wit's end on this.
Here is the ex43_engine.py code which I run to start the game.
from ex43_map import *
import ex43_map
import inspect
#Not sure if this part is neccessary, generated list of all the classes (rooms) I imported from ex43_map.py, as I thought they might be needed to form a "map"
class_list = []
for name, obj in inspect.getmembers(ex43_map):
if inspect.isclass(obj):
class_list.append(name)
class Engine(object):
def __init__(self, room):
self.room = room
def play(self):
# starts the process, this might need to go inside the loop below
next = self.room
start.transportation_choice()
while True:
print "\n-------------"
# I have tried numerous things here to make it work...nothing has
start = StartRoom()
car = CarRoom()
bus = BusRoom()
train = TrainRoom()
airplane = AirplaneRoom()
terminal = TerminalRoom()
a_game = Engine("transportation_choice")
a_game.play()
And here is the ex43_map.py code
from sys import exit
from random import randint
class StartRoom(object):
def __init__(self):
pass
def transportation_choice(self):
print "\nIt's 6 pm and you have just found out that you need to get to Chicago by tomorrow morning for a meeting"
print "How will you choose to get there?\n"
print "Choices: car, bus, train, airplane"
choice = raw_input("> ")
if choice == "car":
return 'CarRoom'
elif choice == "bus":
return 'BusRoom'
elif choice == "train":
return 'TrainRoom'
elif choice == "airplane":
return 'AirplaneRoom'
else:
print "Sorry but '%s' wasn't a choice." % choice
return 'StartRoom'
class CarRoom(object):
def __init__(self):
print "Welcome to the CarRoom"
class BusRoom(object):
def __init__(self):
print "Welcome to the BusRoom"
class TrainRoom(object):
def __init__(self):
print "Welcome to the TrainRoom"
class AirplaneRoom(object):
def __init__(self):
print "Welcome to the AirplaneRoom"
class TerminalRoom(object):
def __init__(self):
self.quips = [
"Oh so sorry you died, you are pretty bad at this.",
"Too bad, you're dead buddy.",
"The end is here.",
"No more playing for you, you're dead."
]
def death(self):
print self.quips[randint(0, len(self.quips)-1)] # randomly selects one of the quips from 0 to # of items in the list and prints it
exit(1)
Instead of returning a string try returning an object, ie
if choice == "car":
return CarRoom()
It might be a good idea to make a Room class, and derive your other rooms from it.
The Room base class can then have a class variable which automatically keeps track of all instantiated rooms.
I haven't thoroughly tested the following, but hopefully it will give you some ideas:
# getters.py
try:
getStr = raw_input # Python 2.x
except NameError:
getStr = input # Python 3.x
getStr.type = str
def typeGetter(dataType):
def getter(msg):
while True:
try:
return dataType(getStr(msg))
except ValueError:
pass
getter.type = dataType
return getter
getInt = typeGetter(int)
getFloat = typeGetter(float)
getBool = typeGetter(bool)
def getOneOf(*args, **kwargs):
"""Get input until it matches an item in args, then return the item
#param *args: items to match against
#param getter: function, input-getter of desired type (defaults to getStr)
#param prompt: string, input prompt (defaults to '> ')
Type of items should match type of getter
"""
argSet = set(args)
getter = kwargs.get('getter', getStr)
prompt = kwargs.get('prompt', '> ')
print('[{0}]'.format(', '.join(args)))
while True:
res = getter(prompt)
if res in argset:
return res
.
# ex43_rooms.py
import textwrap
import random
import getters
class Room(object):
# list of instantiated rooms by name
ROOMS = {}
#classmethod
def getroom(cls, name):
"""Return room instance
If named room does not exist, throws KeyError
"""
return cls.ROOMS[name]
def __init__(self, name):
super(Room,self).__init__()
self.name = name
Room.ROOMS[name] = self
def run(self):
"""Enter the room - what happens?
Abstract base method (subclasses must override)
#retval Room instance to continue or None to quit
"""
raise NotImplementedError()
def __str__(self):
return self.name
def __repr__(self):
return '{0}({1})'.format(self.__class__.__name__, self.name)
class StartRoom(Room):
def __init__(self, name):
super(StartRoom,self).__init__(name)
def run(self):
print textwrap.dedent("""
It's 6 pm and you have just found out that you need to get to Chicago
by tomorrow morning for a meeting! How will you get there?
""")
inp = getters.getOneOf('car','bus','train','airplane')
return Room.getroom(inp)
class CarRoom(Room):
def __init__(self,name):
super(CarRoom,self).__init__(name)
class BusRoom(Room):
def __init__(self,name):
super(BusRoom,self).__init__(name)
class TrainRoom(Room):
def __init__(self,name):
super(TrainRoom,self).__init__(name)
class PlaneRoom(Room):
def __init__(self,name):
super(PlaneRoom,self).__init__(name)
class TerminalRoom(Room):
def __init__(self,name):
super(TerminalRoom,self).__init__(name)
def run(self):
print(random.choice((
"Oh so sorry you died, you are pretty bad at this.",
"Too bad, you're dead buddy.",
"The end is here.",
"No more playing for you, you're dead."
)))
return None
# create rooms (which registers them with Room)
StartRoom('start')
CarRoom('car')
BusRoom('bus')
TrainRoom('train')
PlaneRoom('airplane')
TerminalRoom('terminal')
.
# ex43.py
from ex43_rooms import Room
def main():
here = Room.getroom('start')
while here:
here = here.run()
if __name__=="__main__":
main()

Categories

Resources