Python style with attributes in class definition - python

I have a class with some attributes I want to use the methods. What is the most pythonic way to use it?. Is there documentation I can read about these styles issues?. I use pylint and pep8 to fix this kind of issues, but these program don't show any errors in this example with the attribute
class A(object):
BLOCK_SIZE = 8192
def func1(self):
print self.BLOCK_SIZE
def func2(self):
print A.BLOCK_SIZE
If I understand correctly this two case are the same, but I don't
know if is better to refer the class name or use the self reference.
Thanks

You should prefer self.
There are two types of fields - Class variables and Object variables.
Class variables are shared in the sense that they are accessed by all objects (instances) of that class. There is only copy of the class variable and when any one object makes a change to a class variable, the change is reflected in all the other instances as well.
Object variables are owned by each individual object/instance of the class. In this case, each object has its own copy of the field i.e. they are not shared and are not related in any way to the field by the same name in a different instance of the same class.
By using A.BLOCK_SIZE - you get class variable while by using self.BLOCK_SIZE you get an attribute of the specific object.
Check this to get a detailed example and explanations.
https://www.ibiblio.org/g2swap/byteofpython/read/class-and-object-vars.html

A class attribute can more or less be used as the default value for an instance attribute. The language allow a class attribute to be read either from the class itself or from any instance of the class, provided the instance has no attribute of same name.
But things go differently for write access: A.BLOCK_SIZE=16384 will change the global class attribute, while self.BLOCK_SIZE=16384 will create (or update) an instance attribute of same name.
Here is a little example with your class:
>>> a = A()
>>> a.func1() # access the class attribute
8192
>>> a.func2() # access the class attribute too
8192
>>> a.BLOCK_SIZE=4096 # create an instance attribute of same name
>>> a.func1() # access the INSTANCE attribute
4096
>>> a.func2() # still access the class attribute
8192
That's the reason why you should under common circonstances use the instance semantics (self.BLOCK_SIZE) because it does not rely on the class name and allows per instance override.
The class semantics A.BLOCK_SIZE should only be used if you want to change the class attribute, or need to use the class attribute even if an instance attribute exists.

Related

Getting private attribute in parent class using super(), outside of a method

I have a class with a private constant _BAR = object().
In a child class, outside of a method (no access to self), I want to refer to _BAR.
Here is a contrived example:
class Foo:
_BAR = object()
def __init__(self, bar: object = _BAR):
...
class DFoo(Foo):
"""Child class where I want to access private class variable from parent."""
def __init__(self, baz: object = super()._BAR):
super().__init__(baz)
Unfortunately, this doesn't work. One gets an error: RuntimeError: super(): no arguments
Is there a way to use super outside of a method to get a parent class attribute?
The workaround is to use Foo._BAR, I am wondering though if one can use super to solve this problem.
Inside of DFoo, you cannot refer to Foo._BAR without referring to Foo. Python variables are searched in the local, enclosing, global and built-in scopes (and in this order, it is the so called LEGB rule) and _BAR is not present in any of them.
Let's ignore an explicit Foo._BAR.
Further, it gets inherited: DFoo._BAR will be looked up first in DFoo, and when not found, in Foo.
What other means are there to get the Foo reference? Foo is a base class of DFoo. Can we use this relationship? Yes and no. Yes at execution time and no at definition time.
The problem is when the DFoo is being defined, it does not exist yet. We have no start point to start following the inheritance chain. This rules out an indirect reference (DFoo -> Foo) in a def method(self, ....): line and in a class attribute _DBAR = _BAR.
It is possible to work around this limitation using a class decorator. Define the class and then modify it:
def deco(cls):
cls._BAR = cls.__mro__[1]._BAR * 2 # __mro__[0] is the class itself
return cls
class Foo:
_BAR = 10
#deco
class DFoo(Foo):
pass
print(Foo._BAR, DFoo._BAR) # 10 20
Similar effect can be achieved with a metaclass.
The last option to get a reference to Foo is at execution time. We have the object self, its type is DFoo, and its parent type is Foo and there exists the _BAR. The well known super() is a shortcut to get the parent.
I have assumed only one base class for simplicity. If there were several base classes, super() returns only one of them. The example class decorator does the same. To understand how several bases are sorted to a sequence, see how the MRO works (Method Resolution Order).
My final thought is that I could not think up a use-case where such access as in the question would be required.
Short answer: you can't !
I'm not going into much details about super class itself here. (I've written a pure Python implementation in this gist if you like to read.)
But now let's see how we can call super:
1- Without arguments:
From PEP 3135:
This PEP proposes syntactic sugar for use of the super type to
automatically construct instances of the super type binding to the
class that a method was defined in, and the instance (or class object
for classmethods) that the method is currently acting upon.
The new syntax:
super()
is equivalent to:
super(__class__, <firstarg>)
...and <firstarg> is the first parameter of the method
So this is not an option because you don't have access to the "instance".
(Body of the function/methods is not executed unless it gets called, so no problem if DFoo doesn't exist yet inside the method definition)
2- super(type, instance)
From documentation:
The zero argument form only works inside a class definition, as the
compiler fills in the necessary details to correctly retrieve the
class being defined, as well as accessing the current instance for
ordinary methods.
What were those necessary details mentioned above? A "type" and A "instance":
We can't pass neither "instance" nor "type" which is DFoo here. The first one is because it's not inside the method so we don't have access to instance(self). Second one is DFoo itself. By the time the body of the DFoo class is being executed there is no reference to DFoo, it doesn't exist yet. The body of the class is executed inside a namespace which is a dictionary. After that a new instance of type type which is here named DFoo is created using that populated dictionary and added to the global namespaces. That's what class keyword roughly does in its simple form.
3- super(type, type):
If the second argument is a type, issubclass(type2, type) must be
true
Same reason mentioned in above about accessing the DFoo.
4- super(type):
If the second argument is omitted, the super object returned is
unbound.
If you have an unbound super object you can't do lookup(unless for the super object's attributes itself). Remember super() object is a descriptor. You can turn an unbound object to a bound object by calling __get__ and passing the instance:
class A:
a = 1
class B(A):
pass
class C(B):
sup = super(B)
try:
sup.a
except AttributeError as e:
print(e) # 'super' object has no attribute 'a'
obj = C()
print(obj.sup.a) # 1
obj.sup automatically calls the __get__.
And again same reason about accessing DFoo type mentioned above, nothing changed. Just added for records. These are the ways how we can call super.

How to make a parent class object referring to a child class?

Like in java or C# where we can create an object like
Account obj = new SavingsAccount();
where Account is the parent class and SavingsAccount is the child class
How do I do the same thing with python?
basically I'm trying to do is this
https://repl.it/#MushfiqurRahma1/Polymorphic-Object
Python is dynamically typed: names refer to objects without any notion of type being involved. You just have
>>> class Account: pass
...
>>> class SavingsAccount(Account): pass
...
>>> obj = SavingsAccount()
Each object stores a reference to its own type
>>> type(obj)
<class '__main__.SavingsAccount'>
and each type has a referent to its method resolution order (MRO)
>>> type(obj).__mro__
(<class '__main__.SavingsAccount'>, <class '__main__.Account'>, <class 'object'>)
Instance attributes are not "compartmentalized" according to the class that "defines" them; each attribute simply exists on the instance itself, without reference to any particular class.
Methods exist solely in the classes themselves; when it comes time to call obj.foo(), the MRO is used to determine whose definition is used.
Python uses Duck typing, which means you shouldn’t really care about the class in the Left side of the assignment. Just instantiate the Child class and you should already be able to use it’s parent “interface” to do stuff like dynamic method calls.
Python is a loosely typed language. That means you don't have to specify your variable types. You could just do something like:
class Account():
def deposit():
pass
class SavingsAccount(Account):
pass
obj = SavingsAccount()
obj.deposit(20)
EDIT: As chepner pointed out: Python is strongly typed, but also dynamically typed: type is associated with an object, not the name referring to an object.

Getting the name of a class which has a meta class

Suppose I define a class A with a meta class like this:
class Meta(type):
pass
class A(metaclass=Meta):
pass
Then, when I try to access the name of class A I get the name of the meta class:
A.__class__.__name__
# 'Meta'
However, shouldn't it give me A, my defined class?
Note: I tried to use A.__mro__[0].__name__ and it does give me A, but I am still confused why A.__class__ gives me the meta class name. Does anyone has an explanation of this?
The __class__ dunder reports:
the class to which a class instance belongs.
Quote from instance.__class__
The class A belongs to the class of it's metaclass - only instances of A belong to the class A itself.
a = A()
print(a.__class__.__name__) # 'A'
print(A.__class__.__name__) # 'Meta'
class P: pass
print(P.__class__.__name__) # type
print(P().__class__.__name__) # P
To get the name of the class itself simply use
A.__name__
if you really need it.
I am still a way to groke all of answer to What are metaclasses in Python? - maybe it helps you out.
A is already the class - its name is under A.__name__.
If you try A.__class__.__name__ you will get to the class of which A is instance (that is, its metaclass), name.
A.__mro__[0].__name__ will follow the "method resolution order" for the class A - the __mro__ object is a tuple with all the class hyerarchy that starts in the defined class itself and ends in object. So, A.__mro__[0] will always be A itself - and A.__mro__[0].__name__ is the same as A.__name__.
The __name__ and __qualname__ attributes are writable attributes: changing { __qualname__ after the class is created will change the default __repr__ for instances of that class, for example. Although they are in the language definition and "live" in slots in the class (not on it's dictionary), it is possible to create a __name__ property (I mean, the built-in property object, or any other descriptor) on the metaclass that will dynamically change the __name__ attribute of a class (but not __qualname__ - this must be an attribute of the class, and must be a string)

Why I can Assign a attributes to a class instance, even this attributes is not defined in class?

What kind of mechanism is behind this?
For example, I can define a class
class test:
string_a="aaa"
then I can setup a instance for class test.
test_instance=test()
later I can assign a test_attr to test_instance.
test_instance.test_attr="bbb"
print test_instance.test_attr
this will print, "bbb"
so what is behavior for? anything conflict to __init__?
Thanks.
There are two things going on here:
Class attributes
Instance attributes
It is important to distinguish the difference.
Class Attributes as in:
class A:
a = 1
will persist with every Instance created. That is:
a = A()
b = A()
a.a == b.a # True
Instance Attributes are typically accessed by magic methods called __setattr__ (to set them) and __getattr__ (to retrieve them).
All instances of classes (unless you use __slots__) have an internal dict called __dict__. You don't have to "declare" attributes on a class in order to set them on an instance.
Have a read of Python Data Model

Python's "class is a dict" theory and consequences of adding class variables

Just out of curiosity I'm playing with the __dict__ attribute on Python classes.
I read somewhere that a "class" in python is kind of a dict, and calling __dict__ on a class instance translate the instance into a dict... and I thought "cool! I can use this!"
But this lead me to doubts about the correctness and security of these actions.
For example, if I do:
class fooclass(object):
def __init__(self, arg):
self.arg1 = arg
p = fooclass('value1')
print(p.__dict__)
p.__dict__['arg2'] = 'value2'
print(p.__dict__)
print(p.arg2)
I have this:
>>>{'arg1': 'value1'}
>>>{'arg1': 'value1', 'arg2': 'value2'}
>>>value2
and that's fine, but:
Does the fooclass still have 1 attribute? How can I be sure?
Is it secure to add attributes that way?
Have you ever had cases where this came in handy?
I see that I can't do fooclass.__dict__['arg2'] = 'value2'.. so why this difference between a class and an instance?
You are altering the attributes of the instance. Adding and removing from the __dict__ is exactly what happens for most custom class instances.
The exception is when you have a class that uses __slots__; instances of such a class do not have a __dict__ attribute as attributes are stored in a different way.
Python is a language for consenting adults; there is no protection against adding attributes to instances with a __dict__ in any case, so adding them to the dictionary or by using setattr() makes no difference.
Accessing the __dict__ is helpful when you want to use existing dict methods to access or alter attributes; you can use dict.update() for example:
def Namespace(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
It is also helpful when trying to read an attribute on the instance for which there is a corresponding data descriptor on the class; to bypass the latter you can access the __dict__ on the instance to get to the attribute. You can also use this to test for attributes on the instance and ignore anything the class or the base classes might define.
As for fooclass.__dict__; you are confusing the class with the instances of a class. They are separate objects. A class is a different type of object, and ClassObj.__dict__ is just a proxy object; you can still use setattr() on the class to add arbitrary attributes:
setattr(fooclass, 'arg2', 'value2')
or set attributes directly:
fooclass.arg2 = value2
A class needs to have a __dict__ proxy, because the actual __dict__ attribute is a descriptor object to provide that same attribute on instances. See What is the __dict__.__dict__ attribute of a Python class?
Details about how Python objects work are documented in the Datamodel reference documentation.

Categories