I have a class inside a class like this:
class Robot(object):
def __init__(self):
self.name = 'ganken'
class Speaker(object):
pass
Let’s say I want to access Robot.name from Speaker, how do I do that
You can make Speaker a sub class of Robot:
class Robot(object):
def __init__(self):
self.name = 'ganken'
class Speaker(Robot):
def get_name(self):
return self.name
When you make an instance of Speaker, it will now have the name ganken associated with it.
s = Speaker
s.get_name()
Output:
'ganken'
You could have also called s.name and it would have still returned 'ganken', but usually you want to define functions in your class to get and set different attributes of your class (look up getters and setters if you want more information).
I might have misunderstood your question. If you want to access the name of a instance of a Robot class in your Speaker class (and don't necessarily want to make Speaker a subclass of Robot), you could define your class as:
class Robot(object):
def __init__(self):
self.name = 'ganken'
class Speaker(object):
def get_robot_name(self, robot):
return robot.name
Notice that you can pass a robot instance into the get_robot_name function in this example.
Demo:
r = Robot()
s = Speaker()
s.get_robot_name(r)
'ganken'
You could try the following where you create a speaker object in the Robot class if you are looking for a nested class solution. But i don't know what is the reason for you to do this though.
Try this assuming Python3 environment:
class Robot(object):
def __init__(self):
self.name = 'ganken'
self.speaker = self.createSpeaker()
def createSpeaker(self):
return Robot.Speaker(self)
class Speaker:
def __init__(self,robot):
self.robot = robot
def say_name(self):
return f'Name: {self.robot.name}'
robot = Robot()
print(robot.speaker.say_name())
Try the below:
print(Robot().name)
Just call it then access it
Output is:
ganken
Related
Not sure what I am doing wrong here when trying to initiate instance of object and set a name to the instance...
class Person:
def Person(self, name):
def __init__(self, name):
self.name = name
m = Person.Person('James')
m.name
Any help with an explanation?
I've personally not encountered a situation where the init function is nested beneath a parent function...
The problem here is correlated to the function definition. Basically, you are calling Person function that define but doesn't call the init one. So, you can solve like that:
class Person:
def __init__(self, name):
self.name = name
m = Person('James')
m.name
I have the code something like:
class ClassPrintable:
#classmethod
def print_class(cls):
print(cls)
I would like to be able to derive classes from this, and furthermore call the class methods inline from the class body, eg.
class MyClass(ClassPrintable):
print_class()
Unfortunately this doesn't work, however this does:
class MyClass(ClassPrintable):
ClassPrintable.print_class()
Unfortunately, of course, it prints the class for ClassPrintable rather than MyClass
The obvious solution, doesn't work, eg.
class MyClass(ClassPrintable):
MyClass.print_class()
Python complains it can't find MyClass! with a NameError: name 'MyClass' is not defined
How can I access MyClass's class method from within the body of its definition? I would prefer not to use dynanic metaprogramming but I will if I have to.
You cannot invoke anything on the class before it exists which is only after the class definition (note that method bodies aren't evaluated at class definition time). In Python >= 3.6, you can do the following, using the __init_subclass__ hook:
class ClassPrintable:
#classmethod
def print_class(cls):
print(cls)
#classmethod
def __init_subclass__(cls):
cls.print_class()
class MyClass(ClassPrintable):
pass
Alright I figured it out with small amount of metaprogramming. Whoever thought of __init_subclass__ is a genius. If anyone can see anything drastically wrong with this let me know.
import copy
class Model:
def __init__(self, name, default):
self.model_name = name
self.model_default = default
self.observers = []
class Models():
model_dictionary = {}
def __init_subclass__(cls, models=[]):
setattr(cls, "model_dictionary", {})
for model in models:
cls.model_dictionary[model[0]] = Model(model[0], model[1])
for c in cls.__bases__:
cls.add_base_models(c)
#classmethod
def add_base_models(cls, base):
if hasattr(base, "model_dictionary"):
for model in base.model_dictionary.values():
cls.model_dictionary[model.model_name] = copy.copy(base.model_dictionary[model.model_name])
for c in base.__bases__:
cls.add_base_models(c)
#classmethod
def listen(cls, name, closure):
cls.model_dictionary[name].observers.append(closure)
def __init__(self):
for model in self.model_dictionary.values():
super().__setattr__(model.model_name, model.model_default)
def __setattr__(self, name, value):
if name in self.__class__.model_dictionary.keys():
orig_value = getattr(self, name)
if value != orig_value:
for observer in self.model_dictionary[name].observers:
observer(self, value)
super().__setattr__(name, value)
else:
super().__setattr__(name, value)
Sample use of the code:
class Mouse(Models, models=[("x", 100), ("y", 200), ("visible", True)]):
pass
class SpecialMouse(Mouse, models=[("anger_level", "hostile")]):
pass
mouse = SpecialMouse()
mouse.listen("anger_level", lambda mouse, value : print(value))
mouse.anger_level = "cold!"
mouse.anger_level = "warm"
mouse.anger_level = "warm"
mouse.anger_level = "furious"
Prints out:
cold!
warm
furious
I have some code that looks like this:
class Log(object):
#property
def log(self):
return self.log
class ExampleClass2(ExampleClass, Log):
class ExampleClass3(object):
#property
def log_value(self):
self.log.info('Hi!')
However I'm getting an error,
'ExampleClass3' object has not attribute 'log'
I'm guessing I need to add an __init__() method to DEF, and I've tried using
super(ExampleClass2.ExampleClass3, self).__init__()
but I'm still having problems accessing log. Any suggestions?
I believe to get your desired behavior, you need need to pass in an instance of ExampleClass2 when you create an instance of ExampleClass3.
class OuterClass:
def __init__(self, value):
self.value = value
class InnerClass:
def __init__(self, instance):
self.instance = instance
def inner_print_value(self):
print self.instance.value
def outer_print_value(self):
printer = OuterClass.InnerClass(self)
printer.inner_print_value()
OuterClass('Hi').outer_print_value() # 'Hi'
As noted in the comments, there is rarely a reason for this kind of structure. It would be easier to create InnerClass outside of the definition of OuterClass.
class OuterClass:
def __init__(self, value):
self.value = value
def outer_print_value(self):
printer = InnerClass(self)
printer.inner_print_value()
class InnerClass:
def __init__(self, instance):
self.instance = instance
def inner_print_value(self):
print self.instance.value
It seems like you're expecting the value of self to be augmented when creating an inner-class, but this is not the case. To do this, you'd want to use inheritance, and that doesn't require nested classes either.
I have a quick question regarding python classes.
The following is the setup:
I have one class as the "mastermind" class which contains various instances of other classes. Now these classes need to call a method of that other class, but I don't know how to do that. For example:
class mastermind(object):
def __init__(self):
self.hand = hand()
def iNeedToCallThisMethod(self, funzies):
print funzies
class hand(object):
def __init(self):
pass #here should be a call to the mastermind instance's method
example = mastermind()
Hope you guys can help me with that, my brain is steaming! Thanks a lot!
If you want to call mastermind's method, you need to have a reference to it.
For example
class mastermind(object):
def __init__(self):
self.hand = hand(self)
def iNeedToCallThisMethod(self, funzies):
print funzies
class hand(object):
def __init__(self, mastermind)
mastermind.iNeedToCallThisMethod('funzies')
If you need to call iNeedToCallThisMethod from the __init__ of hand, you should probably put the method in that class.
However, what you want to do can be achieved using a classmethod:
class mastermind(object):
def __init__(self):
self.hand = hand()
#classmethod
def iNeedToCallThisMethod(cls, funzies):
print funzies
class hand(object):
def __init__(self):
mastermind.iNeedToCallThisMethod('funzies')
example = mastermind()
Both object need a reference to one another, try passing the instance to the constructor.
class Mastermind(object):
def __init__(self):
self.hand = Hand(self)
def test_funct(self, arg):
print arg
class Hand(object):
def __init(self, mastermind):
self.mastermind = mastermind
self.mastermind.test_funct()
class hand(object):
def __init(self, other_class):
#here should be a call to the mastermind instance's method
other_class.iNeedToCallThisMethod()
m_m = mastermind()
example = hand(m_m) # passes mastermind to new instance of hand
I'd just pass the object like above
I have a Category class which has different names for each categories, the names of the categories can be unknown, good and bad, all categories share the same behavior so i don't want to create sub classes for each type of category, the problem comes when i am trying to
create the different categories in this way:
Category.GOOD
This statement should return a category object with his name setting to 'good' so i try
the following:
class Category(object):
def __init__(self, name):
self.name = name
#property
def GOOD(self):
category = Category(name='good')
return category
#property
def BAD(self):
category = Category(name='bad')
return category
Then i created and use the category with the following output:
c = Category.GOOD
c.name
AttributeError: 'property' object has no attribute 'name'
Realizing that this doesn't work i try a java like approach:
class Category(object):
GOOD = Category(name='good')
BAD = Category(name='bad')
def __init__(self, name):
self.name = name
What i get here is a undefined name "Category" error, so my question is if there is a pythonic way to create a category object like this.
You probably want to use classmethods:
class Category(object):
#classmethod
def GOOD(cls):
category = cls(name='GOOD')
return category
Now you can do c = Category.GOOD().
You cannot do this with a property; you either have to use a classmethod, or create your own descriptor for that:
class classproperty(property):
def __get__(self, inst, cls):
return self.fget(cls)
I'm abusing the property decorator here; it implements __set__ and __del__ as well, but we can just ignore those here for convenience sake.
Then use that instead of property:
class Category(object):
def __init__(self, name):
self.name = name
#classproperty
def GOOD(cls):
return cls(name='good')
#classproperty
def BAD(cls):
return cls(name='bad')
Now accessing Category.GOOD works:
>>> Category.GOOD
<__main__.Category object at 0x10f49df50>
>>> Category.GOOD.name
'good'
I'd use module variables for this. Consider you have the module category.py:
class Category(object):
# stuff...
now you put the two global objects in it:
GOOD = Category(name='good')
BAD = Category(name='bad')
You can use it like that:
from path.to.category import GOOD, BAD
I don't say that this is pythonic but I think this approach is elegant.
The main point that you could not use class definition inside that class definition itself. So the most straight way to achieve what you are want is to use class/static methods as shown below, or even package constants.
class Category(object):
def __init__(self, name):
self.name = name
#classmethod
def GOOD(cls):
return Category(name='good')
#classmethod
def BAD(cls):
return Category(name='bad')
print Category.GOOD().name
or
class Category(object):
def __init__(self, name):
self.name = name
#staticmethod
def GOOD():
return Category(name='good')
#staticmethod
def BAD():
return Category(name='bad')
print Category.GOOD().name