passing data from one class method to another class method - python

I am learning Python and classes and i encountered a challenge that i need help with.
When I try to pass data from one class method to another class method i run into an error that tells me it misses a positional argument
example:
class Student:
def __init__(self, name):
self.name = name
def pass_to_dl(self):
DataLayer.Datalayer.create_dict_entry(self.name)
class Datalayer:
def __init__(self):
self.dict = {}
def create_dict_entry(self, name):
new_name = {"name":name}
self.dict.update(new_name)
new_student = Student("some_name")
The error I get is:
TypeError: create_new_student() missing 1 required positional argument:
i assume it is because create_dict_entry expects two arguments and I am sending only one
now i can overcome this if i do two things
make the create_dict_entry in DataLayer static
move the dict outside of the DataLayer class
but truthfully that does not seem like the right approach to me i think
i would love to hear your view on this matter
thnx
ps. i dont want student to inherit from DataLayer as i consider DataLayer a non related class that just holds data that i acquire form classes such as Student, Teacher etc

Not sure it's the way you want to go, but if I understand correctly what you're trying to do, I think you want to use inheritance.
class Datalayer:
def __init__(self):
self.dict = {}
def create_dict_entry(self, name):
new_name = {"name":name}
self.dict[self.name] = new_name
class Student(Datalayer):
def __init__(self, name):
super().__init__()
self.name = name
def pass_to_dl(self):
self.create_dict_entry(self.name)
So in this example, Student is your "child class" (that's why you pass Datalayer as its argument). In your __init__, you want to call super(Student, self).__init__() (or just super().__init__() to instantiate your "parent class", Datalayer so you can access trigger its __init__ function.
You may want to read some other examples to understand it properly (https://realpython.com/python-super/).

Related

Share variables within the instance of a class without adding them as attributes

I need to create an instance specific URL based on the argument given to create the instance. This URL has to be available to all methods of my class, but I don't want the URL to be an attribute of the instance itself.
This is what I have:
class Person(object):
def __init__(self, name):
self.name = name
self.url = f'https://stackoverflow/{name}/'
def methodA(self):
self.result1 = parse(self.url, do sth)
def methodB(self):
self.result2 = parse(self.url, do sth else)
This would be an improvement but wouldn't fulfill the DRY principle:
class Person(object):
def __init__(self, name):
self.name = name
def methodA(self):
url = f'https://stackoverflow/{self.name}/'
self.result1 = parse(url, do sth)
def methodB(self):
url = f'https://stackoverflow/{self.name}/'
self.result2 = parse(url, do sth else)
Isn't there something in between?
I thought about defining a method which deletes unwanted runtime attributes after adding them to self, but that's probably not best practice?
For the context: The example above is heavily simplified. The real world example is about several parsed objects of the response which are being used multiple times.
In addition to making it private, as #plalx mentioned, it seems like you also want to make it dynamic relative to self.name, which you can do by making it a property. For example:
class Person(object):
def __init__(self, name):
self.name = name
#property
def _url(self):
return f'https://stackoverflow/{self.name}/'
def methodA(self):
self.result1 = parse(self._url)
def methodB(self):
self.result2 = parse(self._url)
Unfortunately there's no access modifiers in Python so you can't make the variable private to the class like you would in languages like Java for instance. However, you can still indicate that it's meant to be an internal variable by using the _ or __ prefixes.
Another option would be to nest your class declaration within an enclosing function which is an approach commonly used to create private scopes in JavaScript, but since the URL is dynamic based on the name you'd have to re-compute it every time:
def Person(name):
def urlOf(person):
return f'https://stackoverflow/{person.name}/'
class Person(object):
def __init__(self, name):
self.name = name
def test(self):
print(urlOf(self));
return Person(name)
Person('test').test()

Use outer class instance as self in inner class?

I'm writing a wrapper for the GMAIL API. In this wrapper, I am trying to include subattributes in the "main class" so it more closely follows the below:
Previously, I was use methods such as:
class Foo:
def __init__(self, ...):
# add some attributes
def get_method(self, ...):
return some_stuff
This allows me to do foo.get_method(...). To follow the GMAIL API, I try to do:
class Foo:
def __init__(self, ...):
# add some attributes
#property
def method(self):
class _Method:
#staticmethod
def get(self, ...):
return some_stuff
return _Method()
Which allows me to do foo.method.get(...). The above has some problems, it redefines the class every time, and I have to add #staticmethod above every method as part of it. I do realise that I could create the class at the outer class level, and set a hidden variable for each which then .method returns or creates, but this seems like too much workaround.
tldr: Is it possible to make the instance passed to the inner class as self be the instance of the outer class (I do not wish to have to pass the attributes of the outer class to each inner class).
Instead of sharing the self parameter between classes, you are probably better off just passing the things you need to the constructor of the class you instantiate.
class Messages:
def __init__(self, name):
self.name = name
def method(self, other_arg):
return self.name + other_arg
class Test:
name = "hi"
def __init__(self):
self.messages = Messages(name=self.name)
If you need to pass a lot of information to the constructor and it starts becoming unwieldy, you can do something like split the shared code into a third class, and then pass that between the Test and Messages classes as a single object.
In Python there are all sorts of clever things that you can do with metaclasses and magic methods, but in 99% of cases just refactoring things into different classes and functions will get you more readable and maintainable code.
Users should have an instance of messages, which allows method get. The scetch for code is:
class Messages:
...
def get()
...
class Users:
...
messages = Messages(...)
allows
users = Users()
users.messages.get()
The bad thing in this API is plural names, which is a bad sign for class. If done from scratch you would rather have classes User and Message, which make more sense.
If you have a closer look at GET/POST calls in the API you link provided, you would notice the urls are like UserId/settings, another hint to implement User class, not Users.
self in the methods reference the self of the outer class
maybe this is what you want factory-method
Although the example code I'll provide bellow might be similar to the already provided answers, and the link above to another answer might satify you wish, because it is slight different formed I'll still provide my vision on what you asked. The code is self explanatory.
class User:
def __init__(self, pk, name):
self.pk = pk
self.name = name
self._messages = None
def messages(self):
if self.messages is None:
self._messages = Messages(self.pk)
return self._messages
class Messages:
def __init__(self, usr):
self.usr = usr
def get(self):
return self._grab_data()
def _grab_data(self):
# grab the data from DB
if self.usr == 1:
print('All messages of usr 1')
elif self.usr == 2:
print('All messages of usr 2')
elif self.usr == 3:
print('All messages of usr 3')
one = User(1, 'One')
two = User(2, 'Two')
three = User(3, 'Three')
one.messages().get()
two.messages().get()
three.messages().get()
The messages method approach practical would be the same for labels, history etc.
Edit: I'll give one more try to myself trying to understand what you want to achieve, even though you said that
I have tried numerous things with defining the classes outside of the container class [...]
. I don't know if you tried inheritance, since your inner class me, despite it quite don't represent nothing here, but still looks like you want to make use of its functionality somehow. You said as well
self in the methods reference the self of the outer class
This sounds to me like you want inheritance at the end.
Then the way to go would be (a proximity idea by using inheritance):
class me(object):
def __init__(self):
self.__other_arg = None # private and hidden variable
# setter and getter methods
def set_other_arg(self, new_other_arg):
self.__other_arg = new_other_arg
def get_other_arg(self):
return self.__other_arg
class Test(me):
name = 'Class Test'
#property
def message(self):
other_arg = self.get_other_arg()
if other_arg is not None:
return '{} {}'.format(self.name, other_arg)
else:
return self.name
t = Test()
t.set_other_arg('said Hello')
print(t.message)
# output >>> Class Test said Hello
I think this could be a preferable way to go rather than your inner class approach, my opinion, you'll decide. Just one side note, look up for getter and setter in python, it might help you if you want to stick with the inheritance idea given.

How does this python inheritance hierarchy work? (specific example)

I am working on a code base that uses oop and I am relavtively new to it. My question specifically is, why NewMenuItem not inherit from File?
code bunk to play with code: https://codebunk.com/b/350127244/
"""Build class hierarchy and get values from decendants"""
import inspect
def selftest():
class Menu(object):
def __init__(self):
super(Menu, self).__init__()
self.value = "Menu"
class MenuBar(Menu):
#having object in there makes it a new style object, which allows us to use super
def __init__(self):
super(MenuBar, self).__init__()
self.value = "MenuBar"
class File(MenuBar):
def __init__(self):
Menu.__init__()
super(File, self).__init__()
self.value = "File"
self.FileValue = "File here!"
class New(Menu):
def __init__(self):
Menu.__init__()
pass
class NewMenuItem(Menu):
def __init__(self):
"""
Q 1- Why do I need self here?
Menu.__init__(self)
"""
Menu.__init__(self)
pass
def show_vals(self):
print(self.value)
"""
Q 2 -why wont this work?
def show_vals2(self):
print(self.FileValue)
"""
example = File.New.NewMenuItem()
example.show_vals()
"""
Q 3 - Why do I get this error with this line?
inspect.getmro(example)
AttributeError: 'ManageProduct' object has no attribute '__bases__'
"""
I'm trying to understand what is happening line by line, but what I don't get is why NewMenuItem doesn't inherit from File.
I tried hard-coding the instantiation of File,like so:
File.init()
but then I get an error unless I pass the File object:
File.__init__(File())
I guess what I am struggling with is:
-inheritance trees
-super classes
-why we need to hard-code instantiations in this case
Keep in mind that this is the code I have come across. I am not sure why this is the way it is.
Inheritance and scope are two completely different things. NewMenuItem is defined inside the scope of the class New, inside of the scope of the class File, but it inherits from Menu, which inherits from object. So while NewMenuItem will only be accessible through the class File and again through New, it will inherit its methods from Menu, and super will refer to Menu.

Can a class instance created by another class instance access the creator's members?

I have an instance of a Python class that is creating another, and I'm wondering if the "created" class can access members of the "creator" without passing them in explicitly, or doing something like this:
class Creator(object):
def __init__(self, parameter):
self.parameter = parameter
self.created = Created(self)
class Created(object):
def __init__(self, creator):
self.creator = creator
self.parameter = self.creator.parameter
I guess what I'm trying to do is allow parameter to be accessed by both, except in practice there will be multiple parameters, so passing them all will result in a long list of arguments. My solution right now is to use a Creator as an argument to a Created. Is there a less awkward or smarter way to do this? Maybe put all the parameters in a dictionary?
You can do this, and you've almost done it, with one minor problem:
class Created():
def __init__(self, creator):
self.parameter = self.creator.parameter
There is no self.creator. If you never need to access it again after this __init__ call, just use the creator parameter directly:
class Created():
def __init__(self, creator):
self.parameter = creator.parameter
If you need it later, store it as self.creator, and then of course you can access it there:
class Created():
def __init__(self, creator):
self.creator = creator
self.parameter = self.creator.parameter
… although you probably don't even need to copy over parameter in that case, because you can always get to it as self.creator.parameter.
This is a very common pattern.
The only real downside to this is that if you keep the back-reference around (self.creator = creator), you can't have any __del__ methods. (Or you need to make it a weakref, or an indirect reference, like a key that can be used to look up the creator as needed.)
Another option
class Creator():
def __init__(self, parameter):
self.parameter = parameter
self.created = Created()
self.created.creator = self
IMHO it looks fine. If parameter is a class attribute instead of instance, you can use inheritance, but I don't think it is such an improvement.
def creator_factory(param):
class Creator(object):
parameter = param
return Creator
Creator = creator_factory(something)
class Created(Creator):
pass
>>> Created().parameter
'something'

initialize base class with variable not coming from derived class

I'm trying to provide framework which allows people to write their own plugins. These plugins are basically derived classes. My base class needs some variables to initialize, how can I initialize my base class without having to let my derived class feed the variable in the base class initialization?
#!/bin/python
class BaseClass():
def __init__(self,config):
self.config=config
def showConfig(self):
print "I am using %s" % self.config
class UserPlugin(BaseClass):
def __init__(self,config):
BaseClass.__init__(self,config)
def doSomething(self):
print "Something"
fubar = UserPlugin('/tmp/config.cfg')
fubar.showConfig()
My goal is to avoid the need to define the config parameter in the UserPlugin class, since this is something I don't want the user who writes a plugin to be bothered with.
You can use argument lists to pass any remaining arguments to the base class:
class UserPlugin(BaseClass):
def __init__(self, *args, **kwargs):
BaseClass.__init__(self, *args, **kwargs)
Based on your Pastebin code, how about this? This avoids using a separate global, instead using a class attribute, which is accessible as a member to all derived classes and their instances.
#!/bin/python
class BaseClass():
config = '/tmp/config.cfg'
def __init__(self):
pass
def showConfig(self):
print "I am using %s" % self.config
class UserPlugin(BaseClass):
def __init__(self):
BaseClass.__init__(self)
def doSomething(self):
print "Something"
fubar = UserPlugin()
fubar.showConfig()
This was the other way to do it that I mentioned before. Keep in mind that if you want to change the value of BaseClass.config itself, you should access it directly (i.e. BaseClass.config = '/foo/path'; otherwise, you wind up creating a custom UPinstance.config value, leaving BaseClass.config unchanged.

Categories