Why won't my python class inherit from another class? - python

class Media_Work(object):
def __init__(self):
_id: int = 0
_IdDict: {}
_titleDict: {}
class Poem(Media_Work):
def __init__(self, id, title, author, content, age, mtype, verbs):
super().__init__()
self.id = id
self.title = title
self.author = author
self.content = content
self.age = age
self.mtype = mtype
self.verbs = verbs
Poem._IdDict.update({id: self})
My error message:
AttributeError: 'Poem' object has no attribute '_IdDict'
Similar error if I change the bottom line to:
self._IdDict.update({id:self})
New error message:
AttributeError: type object 'Poem' has no attribute '_IdDict'

It is inheriting, but you haven't defined the _IdDict class attribute. _IdDict: {} is type hint, not a definition, and it's a local name in __init__, not a class attribute.
Here's an example of how to fix it. You might need to tailor this to fit your needs:
class Media_Work:
_IdDict = {}
class Poem(Media_Work):
def __init__(self, _id):
Poem._IdDict.update({_id: self})
Example usage:
>>> p = Poem(17)
>>> Poem._IdDict
{17: <__main__.Poem object at 0x7f13689a3ba8>}
>>> Media_Work._IdDict
{17: <__main__.Poem object at 0x7f13689a3ba8>}
By the way, don't use id as a variable name, since it's a builtin.

As-written, your intended dict _IdDict is really a type hint (see comment by ShadowRanger)!
Set the attribute as either a class variable (probably your intention) or assign the attribute in __init__()
Class Variable
Reference will be created in class declaration and shared by all instances of the class
There is no need to call super() to init in this case
class Media_Work():
_id = 0
_IdDict = {}
Attribute
Reference will be created at class init and unique to each instance of the class
class Media_Work():
def __init__(self):
self._id = 0
self._IdDict = {}

[My previous version of this answer was lacking].... You have two issues there. The first is that you failed to assign a value to _IdDict so it was not created. Second, you defined it in the wrong scope. If you wanted to create a Class attribute you need to refer to it as Media_Work._IdDict from within the __init__ method or you needed to define it outside the method. Such as:
class Media_Work(object):
_id: int = 0
_IdDict: {} = {}
_titleDict: {} = {}
def __init__(self):
Media_Work._id = 0

Related

How to define different addresses for class attributes and instance attributes?

How to define different addresses for class attributes and instance attributes?
This problem has bothered me for a long time, unless I delete the definition of the class attribute, but want to use the class attribute.
I have defined a dict with the same name in the class attribute and instance attribute. How can I make the memory address different? I tried a variety of methods to delete the content of the class attribute. Is there any other method?
My demo code is as follows:
class MyClass:
bar: dict = {}
def __init__(self):
bar: dict = {}
print(id(MyClass.bar))
a = MyClass()
print(id(a.bar))
1914627629760
1914627629760
class MyClass:
bar: dict = {}
def __init__(self):
self.bar = {}
print(id(MyClass.bar))
a = MyClass()
print(id(a.bar))
2318292079808
2318295104384
That said, I have no idea why we are doing this, and there is an almost 100% chance this will make whomever (next) maintains this codebase go insane within the next 2 years.
Explanation:
You are not "saving" your variable in your __init__() function.
Try running:
class MyClass:
def __init__(self):
self.a = 1 # setting attribute a to value 1
b = 2 # b is not an attribute, it's just a local variable
m = MyClass()
print(m.a) # this will work
print(m.b) # this will not

How can i add atrributes to the class method from outside class?

This is gonna be my first question on this website. If I do mistake about English, sorry.
Okey, my question is how can I add or set attribute to the class method from outside class?
If i am not wrong,we use settatr() to do this. Can you guys help me pls?
class sss():
def __init__(self,name,surname):
self.name = name
self.surname = surname
def method1(self):
a = "Python"
When we do this:
object = sss("Adam","Roger") #This 2 lines are on outside the class
setattr(object,"Age",19)
What exactly happens? Now this "Age" attribute belong to our object? What is the value of the "Age" attribute now? I mean is that class attribute or what?
My second question is how can I add attribute to the class method?
Thank you
If you instanciate your class with e.g. person = Sss("Adam", "Roger") you can access and add attributes with a dot like this:
person.age = 19
Full example:
class Sss:
# You should name your class with capital letter and without brackets
def __init__(self, name, surname):
self.name = name
self.surname = surname
person = Sss("Adam", "Roger")
print(person.name) # access attribute
person.age = 19 # add attribute
print(person.age)
As #Julian Fock mentioned, you can add attributes to an object by simply writing statements, such as person.age = 19. Your instance/object of the sss-Person class would now have this attribute, but not the class itself.
To modify the class sss-Person itself, you would need to implement some ugly methods, which would be labelled as "hacky" or "bad practices" by most programmers. Companies could refuse to accept the code as valid. You should instead consider using other methods such as class inheritance, where you can derive a modified class adding more advanced functionality.
Still, changing a class definition can be useful for debugging or exceptional emergencies. In that case, you can define external functions, and link them to override your class methods as follows:
# A) Initial Analysis
class Person:
def __init__(self,name,surname):
self.name = name
self.surname = surname
def method1(self):
a = "Python"
person1 = Person("Adam","Roger")
person2 = Person("Adam","Roger")
person1.age = 19
print(person1.age)
# Output: 19
# print(person2.age)
# AttributeError: 'Person' object has no attribute 'age'
# B) Change class attributes
def __init2__(self, name, surname, age):
self.name = name
self.surname = surname
self.age = age
Person.__init__ = __init2__
person3 = Person("Adam","Roger", 20)
print(person3.age)
# Output: 20
# person4 = Person("Adam","Roger")
# TypeError: __init2__() missing 1 required positional argument: 'age'
# C) Overwrite method1
def method2(self):
self.a = "Python"
Person.method1 = method2
person5 = Person("Adam","Roger",44)
person5.method1()
print(person5.a)
# Output: "Python"
What is "Age" attribute now? I mean is that class attribute or what?
Age is now an instance variable on your instance of sss named object.
How can i add attribute to the class method?
I'm not sure what you're asking for here.
PS.: I will not get into the merit of wether it is recommended to do things that I will exemplify here.
Adding to Bill Lynch's answer
What is "Age" attribute now? I mean is that class attribute or what?
Age is now an instance variable on your instance of sss named object.
PS.: Don't use object for it is a keyword in python.
What is the value of the "Age" attribute now?
The value is 19.
I mean is that class attribute or what?
It is a attribute of the instance you created from the class, and only for that instance.
how can I add attribute to the class method?
If you want to add the attribute to the class so that every instance has a new attribute of your choosing, you can do this:
setattr(sss, 'b', 'Python')
obj1 = sss("Adam", "Roger")
print(obj1.b)
obj2 = sss("Adam", "Roger")
print(obj2.b)
obj2.b = "New"
print(obj1.b) # will still be Python
print(obj2.b) # now will be New
If you want to overwrite method1 with a new method:
sss_obj = sss("Adam", "Roger")
def method1(self):
self.b = 'New'
setattr(sss, 'method1', method1)
print('Should be False because the instance has no b attribute yet.', hasattr(sss_obj, 'b'))
sss_obj.method1()
print('Now it has and b value is', sss_obj.b)
If you want to change the method on a specific instance of class sss:
sss_obj = sss("Adam", "Roger")
sss_obj2 = sss("Adam", "Roger")
def method1():
sss_obj2.b = 'New'
setattr(sss_obj2, 'method1', method1)
sss_obj.method1()
print('Should be False because method1 from instance sss_obj does not set b.', hasattr(sss_obj, 'b'))
sss_obj2.method1()
print('Now on instance 2 the b value is', sss_obj2.b)
If you want to change the source of the method as a string programatically:
sss_obj = sss("Adam", "Roger")
new_method_source = 'self.b = "NotRecommended"\n' \
'print("Got into changed method")'
def method1(self):
return exec(
compile(new_method_source, '', mode='exec'),
None,
{
'self': self
}
)
setattr(sss, 'method1', method1)
If you want to change the code from method1, first grab the source with the following line
method1_src = inspect.getsource(sss_obj.method1)
Then change the string as you will and do a similar thing as previously mentioned (compile and stuff).
Cheers

How to get class instance using assigned "id" variable?

Is it possible to get the wooden_sword object using the id variable in the Item class?
class Item:
__ids = count(0)
def __init__(self):
self.id = next(self.__ids)
class Weapon(Item):
def __init__(self, **kwargs):
super().__init__(**kwargs)
wooden_sword = Weapon()
Have the __init__ of Item store to a shared (class attribute) WeakValueDictionary and you can do lookup that way from an alternate constructor (classmethod):
import weakref
class Item:
id_to_item = weakref.WeakValueDictionary()
__ids = count(0)
def __init__(self):
self.id = next(self.__ids)
self.id_to_item[self.id] = self
#classmethod
def from_id(cls, id):
return cls.id_to_item[id]
Item.from_id can raise an exception (probably KeyError like a normal dict; test it) if the object corresponding to that id has been garbage collected; using a plain dict would avoid that issue, though it risks memory "leaks" (not a real leak; the object is available, but might never be used again).

Access class variables from another class

I have a simple class A that gets the name from users.
class A:
def __init__(self, name = ''):
self.name = name
Then I want to create a class B that prints out this name. I tried:
class B:
def print_name(printing_name = A.name):
print(printing_name)
Then I call these methods:
m1 = A("x")
B.print_name(m1)
This returns the error
Traceback (most recent call last):
File "so.py", line 5, in <module>
class B:
File "so.py", line 7, in B
def print_name(printing_name = A.name):
AttributeError: class A has no attribute 'name'
I know that I did not assign a class variable in the class A, and thus the name attribute goes with specific instances, not the entire class. However, the name attribute has to connect with every specific instance because it changes from the case to case. Then how should I get this name in class B?
Change your class B to this:
class B:
#staticmethod
def print_name(obj):
print(obj.name)
The print_name method probably should be decorated as a "static method". The property "name" of self is an instance attribute which can not be referred directly from the class itself.
That's correct: name is an instance attribute, not a class attribute. IN this case, m1 has a name, but class A does not. You need to access the name of the input parameter, not attempt to print a class attribute.
You also need to make B.print_name a class function, since you're not calling it from an instance of B.
class B:
#staticmethod
def print_name(inst):
print(inst.name)
Output:
x
Edit: The answers suggesting #staticmethod are ideal if you understand what it does.
class A:
def __init__(self, name = ''):
self.name = name
class B:
def __init__(self):
pass
def print_name(self, var):
print (var.name)
Output:
>>> m1 = A("X")
>>> b = B()
>>> b.print_name(m1)
X
>>>
In this instance A is the name of the class, and you should not give it as the default argument for calling the print_name method. Have a look at keyword arguments for Python, and you will see that what you have written actually means that you have the default value set to the .name property of the class A, which does not exist unless the class is instantiated (i.e. an object is created of the class).
Your B class should read:
class B:
def print_name(printing_object):
print(printing_object.name)

python descriptors sharing values across classes

A python descriptor that I'm working with is sharing its value across all instances of its owner class. How can I make each instance's descriptor contain its own internal values?
class Desc(object):
def __init__(self, initval=None,name='val'):
self.val = initval
self.name = name
def __get__(self,obj,objtype):
return self.val
def __set__(self,obj,val):
self.val = val
def __delete__(self,obj):
pass
class MyClass(object):
desc = Desc(10,'varx')
if __name__ == "__main__":
c = MyClass()
c.desc = 'max'
d = MyClass()
d.desc = 'sally'
print(c.desc)
print(d.desc)
The output is this, the last call set the value for both objects:
localhost $ python descriptor_testing.py
sally
sally
There is only one descriptor object, stored on the class object, so self is always the same. If you want to store data per-object and access it through the descriptor, you either have to store the data on each object (probably the better idea) or in some data-structure keyed by each object (an idea I don't like as much).
I would save data on the instance object:
class Desc(object):
default_value = 10
def __init__(self, name):
self.name = name
def __get__(self,obj,objtype):
return obj.__dict__.get(self.name, self.default_value)
# alternatively the following; but won't work with shadowing:
#return getattr(obj, self.name, self.default_value)
def __set__(self,obj,val):
obj.__dict__[self.name] = val
# alternatively the following; but won't work with shadowing:
#setattr(obj, self.name, val)
def __delete__(self,obj):
pass
class MyClass(object):
desc = Desc('varx')
In this case, the data will be stored in the obj's 'varx' entry in its __dict__. Because of how data descriptor lookup works though, you can "shadow" the storage location with the descriptor:
class MyClass(object):
varx = Desc('varx')
In this case, when you do the lookup:
MyClass().varx
The descriptor object gets called and can do its lookup, but when the lookup goes like this:
MyClass().__dict__['varx']
The value is returned directly. Thus the descriptor is able to store its data in a 'hidden' place, so to speak.

Categories