classes or objects within a class/object - python

I read related posts but they didn't satisfy me.
Using tkinter i coded a class Musicsheet. Then I wrote classes of notes like wholeNote & halfNote, etc. After instantiaing MusicSheet followed by an instance of WholeNote, it looked ok, with the whole note on the music sheet. However, when I instantiated a half note the whole note had disappeared with just the half note on the sheet;it looks like halfNote had also instantiated ( sub classed?) a new music sheet thus previous notes/objects aren't there. This result in that the latest instance is always the only one on the sheet.
Thanks for the replies. Here are additional info:
class Musicsheet(Frame):
.......TCanvas initialization..
class WholeNote(MusicSheet):
......
class HalfNote(MusicSheet):
def __init__(self,x1,y1,x2,y2)
self.can.create_oval(x1,y1,x2,y2)
.....
more notes classes follow

Based on your current code:
Your note classes are derived from MusicSheet. I suppose you want the notes to be part of the sheet, not an instance of the Sheet. If you derive them from MusicSheet, the init of a Note would create a new instance of MusicSheet, not be added to the MusicSheet.
For Notes i recommend using Tkinter.CanvasItem class for inheritance.
(Type should be polygon for everything that is not a half/full rest or full note)
Using self.can.create_oval should be done in MusicSheet, not in Note Classes, as self.can should refer to the MusicSheet.

Related

Python #property Mapper 'mapped class User->user' has no property 'member_groups

Context: For my work, I'm running a script populate.py where we populate a database. It fails at a certain advanced stage (let's just say step 9) as I'm trying to add a new many to many association table.
I made some changes to correspond to a similar entity that works. Specifically I added some #property and #x.setter methods because I thought that would solve it or at least be good practice. Now I get a failure at an earlier stage that was working before (let's say step 4).
I'm not looking for a solution to either of these issues but an understanding of how there could be such an error as logged with respect to how python is designed to work.
The error is:
`sqlalchemy.exc.InvalidRequestError: Mapper 'mapped class User->user' has no property 'member_groups'`
I did change a class attribute from
member_groups
to
_member_groups
But I also added the following code
#property
def member_groups(self) -> List[MemberGroup]:
return self._member_groups
#member_groups.setter
def member_groups(self, new_member_groups):
'''
Now, what do we do with you?
'''
self._member_groups = new_member_groups
I thought the whole point of the #property decorator in python was for this very use case- that I could call the class attribute _foobar as long as I had the decorator grabbing and setting it correctly..
#property
def member_groups(self):
return self._foobar
..and that it shouldn't make any difference what the class attribute is called as long as we give what we want the user to access it through the property descriptor. I thought that was the whole purpose or at least a major point of this pythonic api, specifically to create pseudo private variables and not have it make a difference or be breaking with existing code, but it shows that mapped class has no property.
I just want the theory, not a code solution per se, although I'll take ideas if you have them. I just want to understand Python better.

Creating Object With A For Loop

Firstly, I do apologise as I'm not quite sure how to word this query within the Python syntax. I've just started learning it today having come from a predominantly PowerShell-based background.
I'm presently trying to obtain a list of projects within our organisation within Google Cloud. I want to display this information in two columns: project name and project number - essentially an object. I then want to be able to query the object to say: where project name is "X", give me the project number.
However, I'm rather having difficulty in creating said object. My code is as follows:
import os
from pprint import pprint
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
credentials = GoogleCredentials.get_application_default()
service = discovery.build('cloudresourcemanager', 'v1', credentials=credentials)
request = service.projects().list()
response = request.execute()
projects = response.get('projects')
The 'projects' variable then seems to be a list, rather than an object I can explore and run queries against. I've tried running things like:
pprint(projects.name)
projects.get('name')
Both of which return the error:
"AttributeError: 'list' object has no attribute 'name'"
I looked into creating a Class within a For loop as well, which nearly gave me what I wanted, but only displayed one project name and project number at a time, rather than the entire collection I can query against:
projects=[]
for project in response.get('projects', []):
class ProjectClass:
name = project['name']
projectNumber = project['projectNumber']
projects.append(ProjectClass.name)
projects.append(ProjectClass.projectNumber)
I thought if I stored each class in a list it might work, but alas, no such joy! Perhaps I need to have the For loop within the class variables?
Any help with this would be greatly appreciated!
As #Code-Apprentice mentioned in a comment, I think you are missing a critical understanding of object-oriented programming, namely the difference between a class and an object. Think of a class as a "blueprint" for creating objects. I.E. your class ProjectClass tells python that objects of type ProjectClass will have two fields, name and projectNumber. However, ProjectClass itself is just the blueprint, not an object. You then need to create an instance of ProjectClass, which you would do like so:
project_class_1 = ProjectClass()
Great, now you have an object of type ProjectClass, and it will have fields name and projectNumber, which you can reference like so:
project_class_1.name
project_class_1.projectNumber
However, you will notice that all instances of the class that you create will have the same value for name and projectNumber, this just won't do! We need to be able to specify values when we create each instance. Enter init(), a special python method colloquially referred to as the constructor. This function is called by python automatically when we create a new instance of our class as above, and is responsible for setting up all the fields of that class. Another powerful feature of classes and objects is that you can define a collection of different functions that can be called at will.
class ProjectClass:
def __init__(self, name, projectNumber):
self.name = name
self.projectNumber = projectNumber
Much better. But wait, what's that self variable? Well, just as before we were able reference the fields of our instance via the "project_class_1" variable name, we need a way to access the fields of our instance when we're running functions that are a part of that instance, right? Enter self. Self is another python builtin parameter that contains a reference to the current instance of the ProjectClass that is being accessed. That way, we can set fields on the instance of the class that will persist, but not be shared or overwritten by other instances of the ProjectClass. It's important to remember that the first argument passed to any function defined on a class will always be self (except for some edge-cases you don't need to worry about now).
So restructuring your code, you would have something like this:
class ProjectClass:
def __init__(self, name, projectNumber):
self.name = name
self.projectNumber = projectNumber
projects = []
for project in response.get('projects', []):
projects.append(ProjectClass(project["name"], project["projectNumber"])
Hopefully I've explained this well and given you a complete answer on how all these pieces fit together. The hope is for you to be able to write that code on your own and not just give you the answer!

Reset sync_reasoner inferences

I am using the owlready2 python module on a local ontology.
I have connected an API endpoint, to submit queries on this ontology.
I need to submit some queries on the original ontology and some other on the updated (with the inferences) ontology.
When I use the sync_reasoner() function, the ontology is updated with the inferences made by HermiT (i.e. the default reasoner).
My issue is, that the inferences made by the reasoner persist among different calls to the attached function.
Is there a workaround to force reset the inferred properties?
def function():
onto = get_ontology("file:///path/file.owl").load()
namespace = onto.get_namespace("http://namespace")
do_operations_with_original_ontology()
with namespace:
sync_reasoner()
do_operations_with_UPDATED_ontology()
return None
Thank you for considering my question,
Argyris
Though I did not use extensively the reasoner functionalities of owlready2 I believe this is the same as for any ontology update using owlready2.
Basically in owlready2, to separate different ontologies or different versions of the same ontology (potentially making use of different namespaces) you need to put them in different "worlds". The syntax is described here.
Here is some code based on the documentation examples to give you an idea of the syntax
from owlready2 import *
world = World()
onto = world.get_ontology("http://test.org/onto.owl")
with onto:
class Drug(Thing):
pass
class ActivePrinciple(Thing):
pass
class has_for_active_principle(Drug >> ActivePrinciple):
pass
class someActivePrinciple(ActivePrinciple):
pass
class MyDrug(Drug):
has_for_active_principle = [someActivePrinciple] #this one has some property restriction
# let's separate the worlds
world2 = World()
onto2 = world2.get_ontology("http://test.org/onto.owl")
with onto2:
class Drug(Thing):
pass
class ActivePrinciple(Thing):
pass
class has_for_active_principle(Drug >> ActivePrinciple):
pass
class someActivePrinciple(ActivePrinciple):
pass
class MyDrug(Thing): # not a subClass of Drug
pass # missing the has_for_active_principle restriction
# now we can save without mixing the ontologies
onto.save(file=r"c:\temp\owlready.rdf", format="rdfxml")
onto2.save(file=r"c:\temp\owlready2.rdf", format="rdfxml")
Note that there is currently a bug which prevents from directly saving the "worlds", only the ontology can be saved, but the bug was already corrected in the development version. See the owlready forum relevant discussion

Should I add new methods to a class instead of using Single Responsibility Principle

We had a seminar where I presented Single Responsibility Principle to my team so that we use it in our projects.
I used the following popular example:
class Employee:
save()
calculate_salary()
generate_report()
And I asked the team to tell whether everything is okay with this class. Everybody told me that it's okay.
But I see three violations of the SRP principle here.
Am I right if I say that all methods should be extracted from the class?
My reasoning:
save() method is a reason for change if change our database.
calculate_salary() method is a reason for change because the salary policy may change.
generate_report() method is a reason for change if we want to change the presentation of the report (i.e. csv instead of html).
Let's take the last method. I came up with the following HtmlReportGenerator class.
class HTMLReportGenerator:
def __init__(self, reportable):
self.reportable = reportable
def generate_csv_report()
class CSVReportGenerator:
def __init__(self, reportable):
self.reportable = reportable
def generate_html_report()
Now even if business logic of this generator changes, it does not touch the Employee class and this was my main point. Moreover, now we can reuse those classes to objects other than Employee class objects.
But the team came up with a different class:
class Employee:
save()
calculate_salary()
generate_html_report()
generate_csv_report()
They understand that they violate the SRP, but it is okay for them.
And this is where I had no other ideas to fight for ))
Any ideas on the situation?
I agree with you, by adding additional functions they violated both the SRP and the open/close principle, and every time there will be a new report type they will violate it again.
I would keep the generate_report() function but add a parameter from interface Type "ReportType" which has a function generate().
This means that for example you can call (pardon my Java):
employee.generate_report(new CSVReport())
employee.generate_report(new HTMLReport())
And tomorrow if you want to add a XML report you just implement XMLReport from the Report interface and call :
employee.generate_report(new XMLReport())
This gives you a lot of flexibility, no need to change employee for new report types, and much easier to test (for example if generate_report had complicated logic you could just create a TestReport class that implements Report interface and just prints to the output stream for debugging and call generate_report(new TestReport()))

python load from shelve - can I retain the variable name?

I'm teaching myself how to write a basic game in python (text based - not using pygame). (Note: I haven't actually gotten to the "game" part per-se, because I wanted to make sure I have the basic core structure figured out first.)
I'm at the point where I'm trying to figure out how I might implement a save/load scenario so a game session could persist beyond a signle running of the program. I did a bit of searching and everything seems to point to pickling or shelving as the best solutions.
My test scenario is for saving and loading a single instance of a class. Specifically, I have a class called Characters(), and (for testing's sake) a sigle instance of that class assigned to a variable called pc. Instances of the Character class have an attribute called name which is originally set to "DEFAULT", but will be updated based on user input at the initial setup of a new game. For ex:
class Characters(object):
def __init__(self):
self.name = "DEFAULT"
pc = Characters()
pc.name = "Bob"
I also have (or will have) a large number of functions that refer to various instances using the variables they are asigned to. For example, a made up one as a simplified example might be:
def print_name(character):
print character.name
def run():
print_name(pc)
run()
I plan to have a save function that will pack up the pc instance (among other info) with their current info (ex: with the updated name). I also will have a load function that would allow a user to play a saved game instead of starting a new one. From what I read, the load could work something like this:
*assuming info was saved to a file called "save1"
*assuming the pc instance was shelved with "pc" as the key
import shelve
mysave = shelve.open("save1")
pc = mysave["pc"]
My question is, is there a way for the shelve load to "remember" the variable name assotiated with the instance, and automatically do that << pc = mysave["pc"] >> step? Or a way for me to store that variable name as a string (ex as the key) and somehow use that string to create the variable with the correct name (pc)?
I will need to "save" a LOT of instances, and can automate that process with a loop, but I don't know how to automate the unloading to specific variable names. Do I really have to re-asign each one individually and explicitly? I need to asign the instances back to the apropriate variable names bc I have a bunch of core functions that refer to specific instances using variable names (like the example I gave above).
Ideas? Is this possible, or is there an entirely different solution that I'm not seeing?
Thanks!
~ribs
Sure, it's possible to do something like that. Since a shelf itself is like a dictionary, just save all the character instances in a real dictionary instance inside it using their variable's name as the key. For example:
class Character(object):
def __init__(self, name="DEFAULT"):
self.name = name
pc = Character("Bob")
def print_name(character):
print character.name
def run():
print_name(pc)
run()
import shelve
mysave = shelve.open("save1")
# save all Character instances without the default name
mysave["all characters"] = {varname:value for varname,value in
globals().iteritems() if
isinstance(value, Character) and
value.name != "DEFAULT"}
mysave.close()
del pc
mysave = shelve.open("save1")
globals().update(mysave["all characters"])
mysave.close()
run()

Categories