Consider such code:
class A ():
name = 7
description = 8
color = 9
class B(A):
pass
Class B now has (inherits) all attributes of class A. For some reason I want B not to inherit attribute 'color'. Is there a possibility to do this?
Yes, I know, that I can first create class B with attributes 'name' and 'description' and then inherit class A from B adding attribute 'color'. But in my exact case, B is actually a reduced version of A, so for me it seems more logical to remove attribute in B (if possible).
I think the best solution would be to change your class hierarchy so you can get the classes you want without any fancy tricks.
However, if you have a really good reason not to do this you could hide the color attribute using a Descriptor. You'll need to be using new style classes for this to work.
class A(object):
name = 7
description = 8
color = 9
class Hider(object):
def __get__(self,instance,owner):
raise AttributeError, "Hidden attribute"
def __set__(self, obj, val):
raise AttributeError, "Hidden attribute"
class B(A):
color = Hider()
You'll then get an AttributeError when you try to use the color attribute:
>>> B.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __get__
AttributeError: Hidden attribute
>>> instance = B()
>>> instance.color
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __get__
AttributeError: Hidden attribute
>>> instance.color = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
AttributeError: Hidden attribute
You can supply a different value for color in B, but if you want B not to have some property of A then there's only one clean way to do it: create a new base class.
class Base():
name = 7
description = 8
class A(Base):
color = 9
class B(Base):
pass
Related
I'm new to using descriptors and I think I have a good understanding on how they work but I have come across a problem and i'm not sure how to fix it.
Code
class Foo:
class Bar:
def __get__(self,instance, owner):
return 10
def __set__(self,instance,value):
raise Exception
bar=Bar()
print(Foo.bar)
Foo.bar=5
print(Foo.bar)
Output
>>> 10
>>> 5
Im trying to make bar a constant for testing purposes, I know about the property decorator but I prefer using descriptors.
First I print out the value of bar to see if __get__ works - and it does, the output is 10.
But then when I assign 5 to bar the expected result would be an exception but instead what happens is 5 gets assigned to bar despite specifying __set__ so when I print again the second output is 5.
Can someone tell me why the error isn't being raised?
From the docs:
object.__set__(self, instance, value)
Called to set the attribute on an instance instance of the owner class to a new value, value.
In your code, Foo.bar = 5 is setting the class attribute, not an instance attribute. If you do use an instance (without first setting Foo.bar = 5, which overrides your descriptor), then you get an exception as expected:
>>> f = Foo()
>>> f.bar
10
>>> f.bar = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
Exception
If you want the __set__ behaviour to apply when the class attribute is set, then the class itself needs to be an instance of a metaclass which uses the descriptor:
class FooMeta(type):
class Bar:
def __get__(self,instance, owner):
return 10
def __set__(self,instance,value):
raise Exception
bar = Bar()
class Foo(metaclass=FooMeta):
pass
Testing:
>>> Foo.bar
10
>>> Foo.bar = 5
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __set__
Exception
I was wondering about classes and how I can access their values from the outside with either printing it or using the _str__ function. I came across this question:
Python function not accessing class variable
Then I did this test in Shell, but it didn't work as expected. I wonder why the other answer worked, but not this one.
(edit)
My question was answered by how to instantiate a class, not instance variables.
>>> class test:
def __init__(self):
self.testy=0
def __str__(self):
return self.testy
>>> a=test
>>> b=test
>>> print(a)
<class '__main__.test'>
>>> a
<class '__main__.test'>
>>> a.testy
Traceback (most recent call last):
File "<pyshell#10>", line 1, in <module>
a.testy
AttributeError: type object 'test' has no attribute 'testy'
>>>
You had done a mistake while creating objects, please find below differences:
class test:
def __init__(self):
self.testy=0
def __str__(self):
return self.testy
a = test()
b = test()
a.testy
output: 0
What you have done:
c = test
d = test
c.testy
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: type object 'test' has no attribute 'testy'
Explanation:
when you are creating objects for a class use object = class_name()
**https://docs.python.org/3/tutorial/classes.html#class-objects
You define your variable inside init, which is only called when the class is instantiated. For a longer explanation, I'd refer you to the first answer on the question you linked.
I'm learning to code in python. Currently, I'm at Obstacles and Classes however I have this issue where the attributes doesn't transfer from parent, and sometimes it oddly works. What seems to be the problem?
>>> class Things:
pass
>>> class Inanimate(Things):
pass
>>> class Animate(Things):
pass
>>> class Animals(Animate):
pass
>>> class Mammals(Animals):
pass
>>> class Giraffes(Mammals):
pass
>>> class Animals(Animate):
def breathe(self):
print("breathes")
>>> class Animals(Animate):
def move(self):
print("moves")
>>> class Animals(Animate):
def eat_food(self):
print("eats food")
>>> class Animals(Animate):
def jump(self):
print("jumps in the air")
>>> class Mammals(Animals):
def feeds_young_with_milk(self):
print("feeds young with milk")
>>> class Giraffes(Mammals):
def eat_leaves_from_trees(self):
print("eat leaves from trees")
>>> reginald = Giraffes()
>>> reginald.move()
Traceback (most recent call last):
File "<pyshell#44>", line 1, in <module>
reginald.move()
AttributeError: 'Giraffes' object has no attribute 'move'
>>> reginal.breathes()
Traceback (most recent call last):
File "<pyshell#45>", line 1, in <module>
reginal.breathes()
NameError: name 'reginal' is not defined
>>> reginald.breathes()
Traceback (most recent call last):
File "<pyshell#46>", line 1, in <module>
reginald.breathes()
AttributeError: 'Giraffes' object has no attribute 'breathes'
>>> reginald.eat_food()
Traceback (most recent call last):
File "<pyshell#47>", line 1, in <module>
reginald.eat_food()
AttributeError: 'Giraffes' object has no attribute 'eat_food'
>>> reginald.jump()
jumps in the air
>>>
Unlike in, say, Ruby, redefining a class doesn't add more stuff to the existing definition. Every time you write class Animals(Animate): ..., you're defining an entirely new class with no connection to any previous class with that name, and replacing the class object that the name Animals used to refer to. The new class doesn't have the methods of the old.
Stop redefining the same classes 5 times. Completely define a class the first time around.
When I access an attribute from the parent class via the child class like this all works fine:
class A():
a=1
b=2
class B(A):
c=3
d=B.a+B.b+B.c
print d
But if I try to access an attribute from the parent class inside the child class like this, it doesn't work:
class A():
a=1
b=2
class B(A):
c=3
d=a+b+c
print d
I receive the error: name 'a' is not defined
Let assume that I have many equation like d=a+b+c (but more complicated) and I can't edit them - I have to call in class B "a" as "a", not "self.a" or "something.a". But I can, before equations, do A.a=a. But it is not the smartest way to reload all variables manually. I want to bypass it using inheritance. Is it possible or i should do all manually? Or maybe it is 3th route in this code?
During the class definition, none of the inherited attributes are available:
>>> class Super(object):
class_attribute = None
def instance_method(self):
pass
>>> class Sub(Super):
foo = class_attribute
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
class Sub(Super):
File "<pyshell#7>", line 2, in Sub
foo = class_attribute
NameError: name 'class_attribute' is not defined
>>> class Sub(Super):
foo = instance_method
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
class Sub(Super):
File "<pyshell#9>", line 2, in Sub
foo = instance_method
NameError: name 'instance_method' is not defined
You can't even access them using super, as the name of the subclass isn't defined within the definition block*:
>>> class Sub(Super):
foo = super(Sub).instance_method
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
class Sub(Super):
File "<pyshell#11>", line 2, in Sub
foo = super(Sub).instance_method
NameError: name 'Sub' is not defined
The only way to access the inherited attributes at definition time is to do so explicitly, using the name of the superclass:
>>> class Sub(Super):
foo = Super.class_attribute
>>> Sub.foo is Super.class_attribute
True
Alternatively you can access them within class or instance methods, but then you need to use the appropriate prefix of the class (conventionally cls) or instance (conventionally self) parameter.
* for anyone thinking "ah, but in 3.x you don't need arguments to super":
>>> class Sub(Super):
foo = super().instance_method
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
class Sub(Super):
File "<pyshell#6>", line 2, in Sub
foo = super().instance_method
RuntimeError: super(): no arguments
That's only true inside instance/class methods!
I may be wrong on this, but are you sure you don't want rather this?
class A(object):
def __init__(self):
self.a = 1
self.b = 2
class B(A):
def __init__(self):
super(B, self).__init__()
self.c = 3
#property
def d(self):
return self.a + self.b + self.c
BB = B()
print BB.d
or, as jonrsharpe pointed out:
class A():
a=1
b=2
class B(A):
c=3
d=A.a+A.b+c
print B.d
Disclaimer This is just an exercise in meta-programming, it has no practical
purpose.
I've assigned __getitem__ and __getattr__ methods on a function object, but
there is no effect...
def foo():
print "foo!"
foo.__getitem__ = lambda name: name
foo.__getattr__ = lambda name: name
foo.baz = 'baz'
Sanity check that we can assign properties to a function:
>>> foo.baz
'baz'
Neat. How about the "magic getters"?
>>> foo.bar
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'
>>> foo['foo']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'function' object is not subscriptable
>>> getattr(foo, 'bar')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'function' object has no attribute 'bar'
Is it possible to have a "magic getter" on a function object?
Nope! Assigning __getitem__ to an instance doesn't work on any type of object:
>>> class A(object):
... pass
...
>>> a = A()
>>> a.__getattr__ = lambda name: name
>>> a.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'foo'
And you can't define __getattr__ on the built-in function type:
>>> import types
>>> types.FunctionType.__getitem__ = lambda name: name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can't set attributes of built-in/extension type 'function'
And you can't subclass types.FunctionType:
>>> import types
>>> class F(types.FunctionType):
... pass
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
type 'function' is not an acceptable base type
At least on new-style classes (which are the only kind in Python 3 and the kind you should be using in Python 2), Python only looks for magic methods on the class (and its ancestors), never on the instance. Docs here.
And of course you can't modify the function type, or derive from it. As you've found, however, any class with a __call__() method makes callable instances, so that's the way to do it.
AHHA! Use __call__, and wrap the function in F()
class F(object):
def __init__(self, fn):
self.__dict__['fn'] = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
def __getitem__(self, name):
return name
def __getattr__(self, name):
return name
>>> foo = F(foo)
>>> f.bar
'bar'
>>> f['foo']
'foo'
>>> foo()
foo!