Implementing sub-objects of the same class - python

My question is pretty general in principle. I have a class called Menu that has a list of items and one or more of those items can be either a string, or another instance of Menu. My code for that looks like this:
class Menu():
def __init__(self):
self.items = []
def add_item(self, item):
self.items.append(item)
def add_menu(self):
self.add_item(Menu())
As you can see I've used the actual name of the class Menu within one of it's functions. My question is if it's possible to do that without writing the actual name of the class, but rather by referring to what class it's defined in? For example, I've tried
self.add_item(super(self))
Which gives TypeError: super() argument 1 must be type, not Menu and also tried
self.add_item(super())
That runs without error, but the object it inserts is <super: <class 'Menu'>, <Menu object>>
I'm beginning to suspect i'm using the wrong tool for the job, and my question is what i'm doing wrong? and is referencing of the type I require even possible?
If it's relevant my python version is 3.5.3

Sure it is possible:
>>> class A:
... def create_instance(self):
... return type(self)()
...
>>> a1 = A()
>>> a2 = a1.add_self()
>>> a1
<__main__.A object at 0x1029c27f0>
>>> a2
<__main__.A object at 0x1029c28d0>
Note, of course, this is because:
>>> type(a1)
<class '__main__.A'>
>>> A
<class '__main__.A'>
>>> type(a1) is A
True
Alternatively, this may also be a use-case for classmethod:
>>> class A:
... #classmethod
... def make_instance(cls):
... return cls()
...
>>> a1 = A()
>>> a2 = a1.make_instance()
>>> a1
<__main__.A object at 0x1029c29b0>
>>> a2
<__main__.A object at 0x1029c27f0>
Now, it is perfectly reasonable for instances of a class to return new instances of the same class, but whether or not it is advisable in your case I don't think I have enough information to give an opinion. But it is certainly possible.

Wrong abstraction: consider creating a class MenuItem for example - that represents single line menu entries, or sub menus. Or some other kind of menu entry that you can't think of today.
In other words: good OOP is about creating helpful abstractions. Menu items can have many different shapes, thus the better answer is not to fit in raw strings, but think up an inheritance hierarchy that supports you solving your problem.

Related

Is it possible to make the output of `type` return a different class?

So disclaimer: this question has piqued my curiosity a bit, and I'm asking this for purely educational purposes. More of a challenge for the Python gurus here I suppose!
Is it possible to make the output of type(foo) return a different value than the actual instance class? i.e. can it pose as an imposter and pass a check such as type(Foo()) is Bar?
#juanpa.arrivillaga made a suggestion of manually re-assigning __class__ on the instance, but that has the effect of changing how all other methods would be called. e.g.
class Foo:
def test(self):
return 1
class Bar:
def test(self):
return 2
foo = Foo()
foo.__class__ = Bar
print(type(foo) is Bar)
print(foo.test())
>>> True
>>> 2
The desired outputs would be True, 1. i.e The class returned in type is different than the instance, and the instance methods defined in the real class still get invoked.
No - the __class__ attribute is a fundamental information on the layout of all Python objects as "seen" on the C API level itself. And that is what is checked by the call to type.
That means: every Python object have a slot in its in-memory layout with space for a single pointer, to the Python object that is that object's class.
Even if you use ctypes or other means to override protection to that slot and change it from Python code (since modifying obj.__class__ with = is guarded at the C level), changing it effectively changes the object type: the value in the __class__ slot IS the object's class, and the test method would be picked from the class in there (Bar) in your example.
However there is more information here: in all documentation, type(obj) is regarded as equivalent as obj.__class__ - however, if the objects'class defines a descriptor with the name __class__, it is used when one uses the form obj.__class__. type(obj) however will check the instance's __class__ slot directly and return the true class.
So, this can "lie" to code using obj.__class__, but not type(obj):
class Bar:
def test(self):
return 2
class Foo:
def test(self):
return 1
#property
def __class__(self):
return Bar
Property on the metaclass
Trying to mess with creating a __class__ descriptor on the metaclass of Foo itself will be messy -- both type(Foo()) and repr(Foo()) will report an instance of Bar, but the "real" object class will be Foo. In a sense, yes, it makes type(Foo()) lie, but not in the way you were thinking about - type(Foo()) will output the repr of Bar(), but it is Foo's repr that is messed up, due to implementation details inside type.__call__:
In [73]: class M(type):
...: #property
...: def __class__(cls):
...: return Bar
...:
In [74]: class Foo(metaclass=M):
...: def test(self):
...: return 1
...:
In [75]: type(Foo())
Out[75]: <__main__.Bar at 0x55665b000578>
In [76]: type(Foo()) is Bar
Out[76]: False
In [77]: type(Foo()) is Foo
Out[77]: True
In [78]: Foo
Out[78]: <__main__.Bar at 0x55665b000578>
In [79]: Foo().test()
Out[79]: 1
In [80]: Bar().test()
Out[80]: 2
In [81]: type(Foo())().test()
Out[81]: 1
Modifying type itself
Since no one "imports" type from anywhere, and just use
the built-in type itself, it is possible to monkeypatch the builtin
type callable to report a false class - and it will work for all
Python code in the same process relying on the call to type:
original_type = __builtins__["type"] if isinstance("__builtins__", dict) else __builtins__.type
def type(obj_or_name, bases=None, attrs=None, **kwargs):
if bases is not None:
return original_type(obj_or_name, bases, attrs, **kwargs)
if hasattr(obj_or_name, "__fakeclass__"):
return getattr(obj_or_name, "__fakeclass__")
return original_type(obj_or_name)
if isinstance(__builtins__, dict):
__builtins__["type"] = type
else:
__builtins__.type = type
del type
There is one trick here I had not find in the docs: when acessing __builtins__ in a program, it works as a dictionary. However, in an interactive environment such as Python's Repl or Ipython, it is a
module - retrieving the original type and writting the modified
version to __builtins__ have to take that into account - the code above
works both ways.
And testing this (I imported the snippet above from a .py file on disk):
>>> class Bar:
... def test(self):
... return 2
...
>>> class Foo:
... def test(self):
... return 1
... __fakeclass__ = Bar
...
>>> type(Foo())
<class '__main__.Bar'>
>>>
>>> Foo().__class__
<class '__main__.Foo'>
>>> Foo().test()
1
Although this works for demonstration purposes, replacing the built-in type caused "dissonances" that proved fatal in a more complex environment such as IPython: Ipython will crash and terminate immediately if the snippet above is run.

Are functions first class objects in python?

I am learning a tutorial on python.It is explaining how functions are first class objects in Python.
def foo():
pass
print(foo.__class__)
print(issubclass(foo.__class__,object))
The output that I get for the above code is
<type 'function'>
True
This program is supposed to demonstrate that functions are first class objects in python? My questions are as follows.
How does the above code prove that functions are fist class objects?
What are the attributes of a first class object?
what does function.__class__ signify? It returns a tuple <type,function> which doesn't mean much?
Here's what Guido says about first class objects in his blog:
One of my goals for Python was to make it so that all objects were "first class." By this, I meant that I wanted all objects that could be named in the language (e.g., integers, strings, functions, classes, modules, methods, etc.) to have equal status. That is, they can be assigned to variables, placed in lists, stored in dictionaries, passed as arguments, and so forth.
The whole blog post is worth reading.
In the example you posted, the tutorial may be making the point that first class objects are generally descendents of the "object" class.
First-class simply means that functions can be treated as a value -- that is you can assign them to variables, return them from functions, as well as pass them in as a parameter. That is you can do code like:
>>> def say_hi():
print "hi"
>>> def say_bye():
print "bye"
>>> f = say_hi
>>> f()
hi
>>> f = say_bye
>>> f()
bye
This is useful as you can now assign functions to variables like any ordinary variable:
>>> for f in (say_hi, say_bye):
f()
hi
bye
Or write higher order functions (that take functions as parameters):
>>> def call_func_n_times(f, n):
for i in range(n):
f()
>>> call_func_n_times(say_hi, 3)
hi
hi
hi
>>> call_func_n_times(say_bye, 2)
bye
bye
About __class__ in python tells what type of object you have. E.g., if you define an list object in python: a = [1,2,3], then a.__class__ will be <type 'list'>. If you have a datetime (from datetime import datetime and then d = datetime.now(), then the type of d instance will be <type 'datetime.datetime'>. They were just showing that in python a function is not a brand new concept. It's just an ordinary object of <type 'function'>.
You proved that functions are first class objects because you were allowed to pass foo as an argument to a method.
The attributes of first class objects was nicely summarised in this post: https://stackoverflow.com/a/245208/3248346
Depending on the language, this can
imply:
being expressible as an anonymous literal value
being storable in variables
being storable in data structures
having an intrinsic identity (independent of any given name)
being comparable for equality with other entities
being passable as a parameter to a procedure/function
being returnable as the result of a procedure/function
being constructible at runtime
being printable
being readable
being transmissible among distributed processes
being storable outside running processes
Regarding your third question, <type 'function'> isn't a tuple. Python's tuple notation is (a,b), not angle brackets.
foo.__class__ returns a class object, that is, an object which represents the class to which foo belongs; class objects happen to produce descriptive strings in the interpreter, in this case telling you that the class of foo is the type called 'function'. (Classes and types are basically the same in modern Python.)
It doesn't mean a whole lot other than that, like any other object, functions have a type:
>>> x = 1
>>> x.__class__
<type 'int'>
>>> y = "bar"
>>> y.__class__
<type 'str'>
>>> def foo(): pass
...
>>> foo.__class__
<type 'function'>
Regarding your comment to #I.K.s answer, f_at_2() in the following would be the method.
def f_at_2(f):
return f(2)
def foo(n):
return n ** n
def bar(n):
return n * n
def baz(n):
return n / 2
funcs = [foo, bar, baz]
for f in funcs:
print f.func_name, f_at_2(f)
...
>>>
foo 4
bar 4
baz 1
>>>
A method is a function of/in a class, but the concept also applies to a function (outside of a class). The functions (as objects) are contained in a data structure and passed to another object.

Check if class object

Is it possible in python to check if an object is a class object. IE if you have
class Foo(object):
pass
How could you check if o is Foo (or some other class) or an instance of Foo (or any other class instance)? In Java this would be a simple matter. Just check if the object is an instance of Class. Is there something similar in python or are you supposed to just not care?
Slight clarification: I'm trying to make a function that prints information about the parameter its given. So if you pass in o, where o = Foo() it prints out information about Foo. If you pass in Foo it should print out the exact same information. Not information about Type.
Use the isinstance builtin function.
>>> o = Foo()
>>> isinstance(o, Foo)
True
>>> isinstance(13, Foo)
False
This also works for subclasses:
>>> class Bar(Foo): pass
>>> b = Bar()
>>> isinstance(b, Foo)
True
>>> isinstance(b, Bar)
True
Yes, normally, you are supposed to not particularly care what type the object is. Instead, you just call the method you want on o so that people can plug in arbitrary objects that conform to your interface. This wouldn't be possible if you were to aggressively check the types of objects that you're using. This principle is called duck typing, and allows you a bit more freedom in how you choose to write your code.
Python is pragmatic though, so feel free to use isinstance if it makes sense for your particular program.
Edit:
To check if some variable is a class vs an instance, you can do this:
>>> isinstance(Foo, type) # returns true if the variable is a type.
True
>>> isinstance(o, type)
False
My end goal is to make a function that prints out information about an object if its an instance and print something different if its a class. So this time I do care.
First, understand that classes are instances — they're instances of type:
>>> class Foo(object):
... pass
...
>>> isinstance(Foo, type)
True
So, you can pick out classes that way, but keep in mind that classes are instances too. (And thus, you can pass classes to functions, return them from functions store them in lists, create the on the fly…)
the isinstance() function
isinstance(o, Foo)
and you can also use it to compare o to object
In [18]: class Foo(object): pass
In [20]: o_instance = Foo()
In [21]: o_class = Foo
In [22]: isinstance(o_instance, Foo)
Out[22]: True
In [23]: isinstance(o_class, Foo)
Out[23]: False
In [24]: isinstance(o_instance, object)
Out[24]: True
In [25]: isinstance(o_class, object)
Out[25]: True
I had to do like Thanatos said and check
isinstance(Foo, type)
But in the case of old class types you have to also do
isinstance(Foo, types.ClassType)

Best of two ways to declare a class variable in Python

The way I usually declare a class variable to be used in instances in Python is the following:
class MyClass(object):
def __init__(self):
self.a_member = 0
my_object = MyClass()
my_object.a_member # evaluates to 0
But the following also works. Is it bad practice? If so, why?
class MyClass(object):
a_member = 0
my_object = MyClass()
my_object.a_member # also evaluates to 0
The second method is used all over Zope, but I haven't seen it anywhere else. Why is that?
Edit: as a response to sr2222's answer. I understand that the two are essentially different. However, if the class is only ever used to instantiate objects, the two will work he same way. So is it bad to use a class variable as an instance variable? It feels like it would be but I can't explain why.
The question is whether this is an attribute of the class itself or of a particular object. If the whole class of things has a certain attribute (possibly with minor exceptions), then by all means, assign an attribute onto the class. If some strange objects, or subclasses differ in this attribute, they can override it as necessary. Also, this is more memory-efficient than assigning an essentially constant attribute onto every object; only the class's __dict__ has a single entry for that attribute, and the __dict__ of each object may remain empty (at least for that particular attribute).
In short, both of your examples are quite idiomatic code, but they mean somewhat different things, both at the machine level, and at the human semantic level.
Let me explain this:
>>> class MyClass(object):
... a_member = 'a'
...
>>> o = MyClass()
>>> p = MyClass()
>>> o.a_member
'a'
>>> p.a_member
'a'
>>> o.a_member = 'b'
>>> p.a_member
'a'
On line two, you're setting a "class attribute". This is litterally an attribute of the object named "MyClass". It is stored as MyClass.__dict__['a_member'] = 'a'. On later lines, you're setting the object attribute o.a_member to be. This is completely equivalent to o.__dict__['a_member'] = 'b'. You can see that this has nothing to do with the separate dictionary of p.__dict__. When accessing a_member of p, it is not found in the object dictionary, and deferred up to its class dictionary: MyClass.a_member. This is why modifying the attributes of o do not affect the attributes of p, because it doesn't affect the attributes of MyClass.
The first is an instance attribute, the second a class attribute. They are not the same at all. An instance attribute is attached to an actual created object of the type whereas the class variable is attached to the class (the type) itself.
>>> class A(object):
... cls_attr = 'a'
... def __init__(self, x):
... self.ins_attr = x
...
>>> a1 = A(1)
>>> a2 = A(2)
>>> a1.cls_attr
'a'
>>> a2.cls_attr
'a'
>>> a1.ins_attr
1
>>> a2.ins_attr
2
>>> a1.__class__.cls_attr = 'b'
>>> a2.cls_attr
'b'
>>> a1.ins_attr = 3
>>> a2.ins_attr
2
Even if you are never modifying the objects' contents, the two are not interchangeable. The way I understand it, accessing class attributes is slightly slower than accessing instance attributes, because the interpreter essentially has to take an extra step to look up the class attribute.
Instance attribute
"What's a.thing?"
Class attribute
"What's a.thing? Oh, a has no instance attribute thing, I'll check its class..."
I have my answer! I owe to #mjgpy3's reference in the comment to the original post. The difference comes if the value assigned to the class variable is MUTABLE! THEN, the two will be changed together. The members split when a new value replaces the old one
>>> class MyClass(object):
... my_str = 'a'
... my_list = []
...
>>> a1, a2 = MyClass(), MyClass()
>>> a1.my_str # This is the CLASS variable.
'a'
>>> a2.my_str # This is the exact same class variable.
'a'
>>> a1.my_str = 'b' # This is a completely new instance variable. Strings are not mutable.
>>> a2.my_str # This is still the old, unchanged class variable.
'a'
>>> a1.my_list.append('w') # We're changing the mutable class variable, but not reassigning it.
>>> a2.my_list # This is the same old class variable, but with a new value.
['w']
Edit: this is pretty much what bukzor wrote. They get the best answer mark.

Can I instantiate a subclass object from the superclass

I have the following example code:
class A(object):
def __init__(self, id):
self.myid = id
def foo(self, x):
print 'foo', self.myid*x
class B(A):
def __init__(self, id):
self.myid = id
self.mybid = id*2
def bar(self, x):
print 'bar', self.myid, self.mybid, x
When used, the following could be generated:
>>> a = A(2)
>>> a.foo(10)
foo 20
>>>
>>> b = B(3)
>>> b.foo(10)
foo 30
>>> b.bar(12)
bar 3 6 12
Now lets say I have some more subclasses class C(A): and class D(A):. I also know that the id will always fit in either B, C or D, but never in 2 of them at the same time.
Now I would like to call A(23) and get an object of the correct subclass. Something like this:
>>> type(A(2))
<class '__main__.B'>
>>> type(A(22))
<class '__main__.D'>
>>> type(A(31))
<class '__main__.C'>
>>> type(A(12))
<class '__main__.B'>
Is this impossible or is it possible but just bad design? How should problems like this be solved?
You should rather implement Abstract Factory pattern, and your factory would then build any object you like, depending on provided parameters. That way your code will remain clean and extensible.
Any hack you could use to make it directly can be removed when you upgrade your interpreter version, since no one expects backwards compatibility to preserve such things.
EDIT: After a while I'm not sure if you should use Abstract Factory, or Factory Method pattern. It depends on the details of your code, so suit your needs.
Generally it's not such a good idea when a superclass has any knowledge of the subclasses.
Think about what you want to do from an OO point of view.
The superclass is providing common behaviour for all objects of that type, e.g. Animal. Then the subclass provides the specialisation of the behaviour, e.g. Dog.
Think of it in terms of an "isa" relationship, i.e. a Dog is an Animal.
An Animal is a Dog doesn't really make sense.
HTH
cheers,
Rob
I don't think you can change the type of the object, but you can create another class that will work like a factory for the subclasses. Something like this:
class LetterFactory(object):
#staticmethod
def getLetterObject(n):
if n == 1:
return A(n)
elif n == 2:
return B(n)
else:
return C(n)
a = LetterFactory.getLetterObject(1)
b = LetterFactory.getLetterObject(2)
...

Categories

Resources