Understanding properties syntax and functionality in Python - python

I'm using python 2.7, I've created a class, and I got some property in it. I'm instantiating an object out of the class with some value input. The class:
class Prop(object):
def __init__(self, data):
self.A = data
and the getter/setter:
#property
def A(self):
return self._A
#A.setter
def A(self, data):
self._A = data
The above works fine. Inside my main script I'm instantiating:
prop_demo = Prop('some text')
However, if I change inside __init__ the self.A = data with self._A = data, then A doesn't pass though the setter, then I think it misses a bit the point of having a setter. I changed it since it seems more intuitive to me to use the _ for an inner class variable, and doing self.A = data seems to me something that an instance of that class should do (outside the class, in my main script).
I have 2 questions:
Why do we do self.A and not self._A in the init? maybe I'm missing something regarding the importance of _ with python class variables?
Why inside the class init we need to call the setter, and the setter is not automatically executed when creating the object? Why isn't the setter automatically being executed when I instantiate an object?

Related

Refer to a superclass from the class body

I've got some code where I need to refer to a superclass when defining stuff in a derived class:
class Base:
def foo(self):
print('foo')
def bar(self):
print('bar')
class Derived_A(Base):
meth = Base.foo
class Derived_B(Base):
meth = Base.bar
Derived_A().meth()
Derived_B().meth()
This works, but I don't like verbatim references to Base in derived classes. Is there a way to use super or alike for this?
You can't do that.
class keyword in Python is used to create classes which are instances of type type. In it's simplified version, it does the following:
Python creates a namespace and executes the body of the class in that namespace so that it will be populated with all methods and attributes and so on...
Then calls the three-arguments form of type(). The result of this call is your class which is then assign to a symbol which is the name of your class.
The point is when the body of the class is being executed. It doesn't know about the "bases". Those bases are passed to the type() after that.
I also explained the reasons why you can't use super() here.
Does this work for you?
class Base:
def foo(self):
print('foo')
def bar(self):
print('bar')
class Derived_A(Base):
def __init__(self):
self.meth = super().foo
class Derived_B(Base):
def __init__(self):
self.meth = super().bar
a = Derived_A().meth()
b = Derived_B().meth()
You'll need to lookup the method on the base class after the new type is created. In the body of the class definition, the type and base classes are not accessible.
Something like:
class Derived_A(Base):
def meth(self):
return super().foo()
Now, it is possible to do some magic behind the scenes to expose Base to the scope of the class definition as its being executed, but that's much dirtier, and would mean that you'd need to supply a metaclass in your class definition.
Since you want "magic", there is still one sane option we can take before diving into metaclasses. Requires Python 3.9+
def alias(name):
def inner(cls):
return getattr(cls, name).__get__(cls)
return classmethod(property(inner))
class Base:
def foo(self):
...
class Derived_A(Base):
meth = alias("foo")
Derived_A().meth() # works
Derived_A.meth() # also works
Yes, this does require passing the method name as a string, which destroys your IDE and typechecker's ability to reason about it. But there isn't a good way to get what you are wanting without some compromises like that.
Really, a bit of redundancy for readability is probably worth it here.

python __init__ class attribute, when will class Test be ready?

First program in book fluent Python. I simplified the program.
class Test:
a = 15
def __init__(self):
self.att = a
t = Test()
print(t.att)
It didn't work, and if i use
self.att = Test.a
then it worked.
Why should I always use Test.a even in class Test?
I learned java, and I don't have to do so.
Also, here comes another problem.
class Test:
a = 15
def __init__(self, b=Test.a):
self.att = b
t = Test()
print(t.att)
It didn't work. I searched on the site.
And is the reason that:
default arguments should be defined while function definition, not till being called?
And since
self.att = Test.a
worked, so function content will be checked for valid till being called?
Am I right?
Class Test will be ready after #1, but before, I used Test.a, because it didn't check function content before being called. right?
class Test:
a = 15
def __init__(self):
self.att = Test.a
#1
t = Test()
print(t.att)
Members like that need to be accessed like this: self.a (even in the __init__ method)
class Test:
a = 15
def __init__(self):
self.att = self.a
t = Test()
print(t.att)
I learned java, and I don't have to do so.
But if you compare Java with Python, you will notice that you will have to prefix self for every instance member, and you actually need your methods to take that self argument in all the time. So you cannot really expect Java behavior here.
But just like with methods or instance variables, you can access class members using self as well:
class Test:
a = 15
def __init__(self):
self.att = self.a
At runtime of __init__, self will refer to the instance of your Test type. So when you want to access anything on the instance, you need to do that on self. For class members, all its members are also visible on the instance (unless they are hidden by instance members), so self.a works just like Test.a works. You can also try this with your created instance t: t.a will also work even though there is no instance member a.
The reason is __init__ is executed on object creation; inside your __init__ function scope there is no a variable defined. How should it know you mean the a defined in the class?
When stating Test.a you explicity tell python to use the class attribute. When you are using self.a Python uses the MRO lookup process to find the a attribute.
class Test:
a = 15
def __init__(self):
self.att = self.a
#1
t = Test()
print(t.att)

Creating a Class Object from the String Name, Python

I have a python module that calls multiple other classes within the same module.
class Main(object):
def foo(self):
return 'bar'
def getClass(self, className, *args):
return eval(className + "(%s)" % ",".join(args))
class A(object):
def __init__(self, b):
self._b = b
class B(object):
def __init__(self, c):
self._c = c
What I would like to do is this. In the class Main, I want to generate a class Object by only knowing the class name, and passing in the variables needed to create that class.
I now I can do conditional if/else, but I was wondering if it was possible. I figured I could use eval() as well, but i heard that can be evil.
Suggestions? Comments? I have a class that references multiple sublcasses, and instead of creating a class for each type, I figured this would be easier to do.
Thank you

When accessing Properties, send to another instance

class x():
def __init__(self):
self.z=2
class hi():
def __init__(self):
self.child=x()
f=hi()
print f.z
I want it to print 2.
Basically I want to forward any calls to that class to another class.
The simplest approach is implementing __getattr__:
class hi():
def __init__(self):
self.child=x()
def __getattr__(self, attr):
return getattr(self.child, attr)
This has certain disadvantages, but it might work for your limited use case. You might want to implement __hasattr__ and __setattr__ as well.
The Python syntax is:
class hi(x):
To say that hi inherit (should be a child of) x.
.
Note: in order for hi to have property z (since this is in hi's __init__) x.__init__ needs to be explicitly run in x. That is,
class hi(x):
def __init__(self):
x.__init__(self)

python: changing object class in multi-level inheritance

I have looked at many questions posted here to find an answer to my problem, but I wasn't successful. The problem might be, that I just don't know for what keywords I should look. So my problem is the following:
I've got a program, that has a multi-level inheritance and I am trying to figure out how the best way would be to change the class of an object to a subclass. Let's say I have the following code:
class A(object):
def __init(self, filename, ..)
super(A, self).__init__()
...some assignments here
class B(A):
def __init(self, filename, ..)
super(B, self).__init__()
...some assignments here
class C(A):
def __init(self, filename, ..)
super(C, self).__init__()
...some assignments here
and so on...
I always want to start initialising an object of class A. Depending on the type of the file that is used, the assignments may differ and depending on those assignments I can determine what kind of file it is. So now I want to change the class of the object to whatever class is suitable..
I know I could pass the A object to B or C and use copy or deepcopy, but in A i am assigning an object of which the reference should not change and some others where it should change. Also I would need to delete that object of A, after initialising B or C.
class B(A):
def __init__(self, filename, objA = None):
if objA is not None:
self.__dict__ = copy.deepcopy(objA.__dict__)
del(objA)
else:
super(B, self).__init__(filename)
Also there is another possibility by changing the _class attribute to another class and use some kind of update method of the new class.
I would like to know, which of the two approaches is recommended or is there even a better one. Thanks in advance.
What you want is a factory: a function that opens the file, reads the stuff it needs to read to figure out what kind of file it is, and then initializes and returns an object of the appropriate class.
If you want to keep it a class, you'd want to override __new__() and then return an object of the desired class instead of its own class. (You could also do it using a metaclass and overriding __call__() on that.)
You can change an instance's class after instantiating it as well, by changing its __class__ attribute to point to the desired class. That'll work, but the factory is going to be more familiar to other programmers who will read your code.
This code will explain what you want to do
class B(object):
def x(self):
print 'b'
class A(object):
def x(self):
print 'a'
Now we create two objects
a = a()
b = b()
a.x()
a
b.x()
b
now if you want 'a' to become a B object
a.__class__ = type(b)
or
a.__class__ = B
now the x attribute is from the B class.
a.x()
b

Categories