0

One thing that I am struggling with while trying to learn concepts of OOP is creation of class instances. Most of the tutorials online will explain basic principles like Init, Self, Inheritance etc.. but when it comes to creating instances of the class itself it is usually reduced to something like that:

emp1 = Employee("John")
emp2 = Employee("Leviticus")

In reality most of us beginners will want to create instance of a class dynamically (On press of button etc..) not directly in code and also will be interessted in keeping track of our instances. What I was able to come up is this:

from tkinter import *
import random

class Point:

    _registry = []

    def __init__(self,  x_pos, y_pos):
        self._registry.append(self) 
        self.x_pos = x_pos
        self.y_pos = y_pos
        print(self.x_pos, self.y_pos)

def create_point():
    Point(random.randint(1,20),random.randint(1,20))

window = Tk()

button = Button(window, text = "Add point", command=create_point)
button.pack()
window.mainloop() 

Can someone advise if this is a proper way to do this? Shouldnt the fnction create_point be within Point class? What is the proper way to keep track of instances and later delete them? Shall I use some sort of ID attribute to keep track and "itemize" my instances? Is there any good source with tutorial that deals with that?

Thank you Jacob

smci
  • 32,567
  • 20
  • 113
  • 146
J.J.
  • 129
  • 2
  • 14
  • Duplicate of [What is a clean, pythonic way to have multiple constructors in Python?](https://stackoverflow.com/questions/682504/what-is-a-clean-pythonic-way-to-have-multiple-constructors-in-python). In this case you don't even need a separate classmethod, you can just have `__init__` use random coords, when called with no args. – smci Jul 17 '18 at 23:22
  • You're getting confused: your question is not so much dynamically creating a new `Point` (simply call the `Point()` constructor, already), but how to have its coords randomized. You don't need a separate `create_point()`, you can accomplish that inside `__init__` when it sees it was called with no args, i.e. in the `__init__` signature make `x_pos = None, y_pos = None` optional. – smci Jul 17 '18 at 23:26
  • But if you *really insisted* on having a separate method, a) make it a @classmethod of Point, b) call it `make_random()` – smci Jul 17 '18 at 23:30
  • 1
    @smci: I've rolled the edit back because I think the question is about keeping track of the created objects, not about the randomness. – user2357112 Jul 17 '18 at 23:32
  • @user2357112: it is utterly unPythonic for a class to do that, for no reason. The OP didn't articulate any reason, either, only *"What is the proper way to keep track of instances and later delete them?"* In Python, you don't need to, because you don't manually delete things. – smci Jul 17 '18 at 23:35
  • @smci: Indeed, keeping track of the objects created by the button should almost certainly be some other code's job. Having the class track things is a newbie trap. – user2357112 Jul 17 '18 at 23:36
  • @user2357112: so the only valid question being asked here is *"Creating Class Instances dynamically with randomized state"*, so please unrollback your rollback... – smci Jul 17 '18 at 23:39
  • 2
    @smci: The thing the question is asking about may be a bad idea, but that doesn't mean we should edit the question to focus on some other aspect of the code. It means that answers should explain why the thing is a bad idea and present alternatives. – user2357112 Jul 17 '18 at 23:43
  • Sorry for confsion. My question really has nothing to do with randomness (shouldnt have inclluded it). I am really just confused with: 1. Create an instance of class in "pythonic way". 2. Keep track of instances and deleting them. Just want to manage set of points (adding, deleting, changing their coordinates). Pretty much just a software to draw points on canavas – J.J. Jul 17 '18 at 23:46
  • J.J: please edit the title. It seems your premise is buried in the third paragraph: *"In reality most of us beginners will want to create instance of a class dynamically not directly in code and also will be interested in keeping track of our (GUI) instances."* (Why on earth? Because that's the way Tcl/Tk does it? In fact even in Java they don't, they use anonymous instances. There simply is no valid reason to need to track the instances. Please tell us why you think there is? Maybe you want to retitle the question *"In Python, do we need to keep track of dynamically-declared instances?"*) – smci Jul 17 '18 at 23:49
  • I tagged this [tag:tkinter]. Please have a read of the other Python tkinter questions e.g. [create an image in the canvas using a button event tkinter](https://stackoverflow.com/questions/13686009/create-an-image-in-the-canvas-using-a-button-event-tkinter). Notice that no GUI object was stored except locally inside a function, and there was a function encapsulating every GUI construction step. So I think you're asking how to write a GUI function which allows you to dynamically declare and add new buttons, **underneath a specific GUI item** (in this case the root window... – smci Jul 18 '18 at 00:01
  • ...but you could parameterize that as fn arg – smci Jul 18 '18 at 00:02
  • 1
    I _wouldn't_ put `create_point` into the `Point` class. It's not relevant to the definition of a point that its coordinates are random. But if you find it convenient to make `create_point` a classmethod, that's ok. Generally, there's no need for a class to track its instances like you do with `_registry`, although sometimes that is useful, eg in my answer [here](https://stackoverflow.com/a/31424836/4014959). BTW, a Point is usually immutable. If you need an object to store a location that can change then I'd probably use a different name, like Location. – PM 2Ring Jul 18 '18 at 09:49

1 Answers1

0

after completing tutorial at: https://pythonschool.net/category/oop.html I managed to get what I wanted by doing:

class Point:

    def __init__(self,ID,  xcor, ycor):

        self._ID = ID
        self._xcor = xcor
        self._ycor = ycor

    def report(self):
        return {"ID:":self._ID,"xcor":self._xcor,"ycor":self._ycor}

    def get_ID(self):
        return self._ID

class Points:

    def __init__(self):
        self._points = []

    def add_point(self, point):
        self._points.append(point)

    def return_index_from_ID(self, ID):

        for i, o in enumerate(self._points):
            if o.get_ID() == ID:    
                break
        return i

    def delete_point(self, index):
        del self._points[index]

    def print_contents(self):
        for x in self._points:
            print(x.report())

    def return_empty_ID(self):

        list = []

        for x in self._points:
            list.append(x.get_ID())

        if not list:
            return 1
        else:
            for i in range(1, max(list)+2):
                if i not in  list: break
            return  i

def add_point( xcor, ycor, points):
    points.add_point(Point(points.return_empty_ID(), xcor, ycor))

def delete_point(ID, points):
    points.delete_point(ID)

Simple main function for testing to show what I was after:

from point_class import *

myPoints = Points()
noexit = True

while noexit:

    print("**********************************************")
    print("0  -  Exit")
    print("1 -  Add Point")
    print("2 -  Print Points")
    print("3 -  Delete Points")
    print("**********************************************")

    choice = int(input("Option selected:   "))

    if choice == 0:
        noexit = False
    elif choice == 1:
        add_point(3,5,myPoints)
    elif choice == 2:
        myPoints.print_contents()
    elif choice == 3:
        ID = int(input("Please insert ID of point:   "))
        delete_point(myPoints.return_index_from_ID(ID),myPoints)
J.J.
  • 129
  • 2
  • 14