Using class method to access class variable within a class - python

I have a question regarding accessing class variable from the class.
Which way is preferred? Why Version 1 works? name isn't instance variable, how it can be accessed using .self?
Version 1:
class Base:
def get_name(self): return self.name
class Child_1(Base):
name = 'Child 1 name'
child = Child_1()
print(child.get_name())
Version 2:
class Base:
#classmethod
def get_name(cls): return cls.name
class Child_1(Base):
name = 'Child 1 name'
child = Child_1()
print(child.get_name())
Motivation behind this, is defining name once for all instances to save space.

self.name by default refers to cls.name
if you set it it only sets it for that instance however
self.name = "bob"
now overrides the class level name
just the same for methods as well
class Foo:
#staticmethod
def hello():
print("Hi There From Foo!")
def __init__(self):
self.hello() #this works
Foo.hello() # this also works
Foo() # print from in the init

Related

Initialized variable not accessable inside of the class

class DemoClass:
def __init__(self):
self.name = "Marko"
def some_method(self):
print(self.name)
print(self.name) # NameError: name 'self' is not defined ???
my_object = DemoClass()
Why does this happen? Didn't I initialize the self.name variable in the init method which I think it means that it should be accessable in the entire class?
class DemoClass:
def __init__(self):
self.name = "Marko"
def some_method(self):
print(self.name)
my_object = DemoClass()
my_object.some_method()
do like this bro then only you can print the name.
You call the print() function with a class attribute (name) as argument. Even though the attribute is defined when Python executes the print() line, the class attributes exist in the local scope of the class and are accessible only to the class members or through the class namespace (e.g. my_object.name in a different scope where my_object is defined).

Class Attributes and Instance Attributes

I am trying to learn the difference between the instance attributes and class attributes and attributes. I have the code below and I am trying to distinguish these factors.
class Student:
firstname = ""
lastname = ""
ucid = ""
department = ""
nationality = ""
courses = {}
def __init__(self, fname, lname, ucid, dept, n):
self.firstname = fname
self.lastname = lname
self.ucid = ucid
self.department = dept
self.nationality = n
self.courses = {}
def setName(self, fname, lname):
self.firstname = fname
self.lastname = lname
def setDepartment(self, d):
self.department = d
def setUcid(self, u):
self.ucid = u
def setNationality(self, n):
self.nationality = n
def addCourse(self, coursename, gpa):
self.courses[coursename] = gpa
def printAll(self):
print("The name of the student is ", self.firstname, self.lastname)
print("nationality and UCID: ", self.nationality, self.ucid)
print("Department: ", self.department)
print("Result: ")
for key in self.courses.keys():
print(key, self.courses[key])
print("--------------------\n")
s1=Student("Beth","Bean","30303","Computer Science","International")
s1.addCourse("SCIENCE",3.75)
s1.printAll()
s2=Student("Mac","Miller","30303","Envr Science","American")
s2.addCourse("MATH",4.00)
s2.printAll()
From what I understood the attributes would be: firstname,lastname,ucid,department,nationality,courses But I do not know what instance attributes and class attributes would be.
I am trying to learn the difference between the instance attributes and class attributes and attributes.
there should be two attributes, class attribute, instance attribute. or instance attribute&none-instance attribute for convenience.
instance attribute
these are things activated only when __init__ has been called.
you can only access thenm after Class is initialized, which commonly seen as self.xxx.
and methods in class with self as its first parameter(normally), these functions are instance methods, and you can only access after you initialized the Class.
and methods in class with #property deco, they are instance attributes
common seen instance attribute
class Name(object):
def __init__(self):
self.age = 100
def func(self):
pass
#property
def age(self):
return self.age
class attribute
non-instance attribute or static attribute, whatever you call it
these things stay activated along with Class.
which means you can access them whenever you need to, like __init__, even in __new__.
they can be called by both Class and instance.
common seen class attribute
class Name(object):
attr = 'Im class attribute'
there is something else you may should know, class method, which stay activated along with Class but the difference is class method can't be called by instance but only Class. example here
class Name(object)
attr = 'Im class attribute'
#classmethod
def get_attr(cls):
return cls.attr
Conclusion
"class attribute" can be called by both instance and Class
"instance attribute" can only called by instance.

Access Child class variables in Parent class

I have a Parent class and a inherited child class, I would like to know how to access the child class variable in my Parent class..
I tried this and it fails -
class Parent(object):
def __init__(self):
print x
class Child(Parent):
x = 1;
x = Child();
Error:-
NameError: global name 'x' is not defined
This question is in relation to Django forms where we inherit the form class
and declare some class variables.
For example:-
My form looks like this
from django import forms
class EmployeeForm(forms.Form):
fname = forms.CharField(max_length=100)
lname = forms.CharField(max_length=100)
I believe the form fields are considered as class variable and somehow passed to the parent class..
Django does this with metaclasses. (Relevant Django source)
Here's a distilled example of the relevant code:
class Field(object):
def __init__(self, *args):
self.args = args
def __repr__(self):
return "Form(%s)" % (', '.join(map(repr, self.args)),)
class Meta(type):
def __new__(mcs, name, bases, attrs):
field_list = []
for k,v in attrs.items():
if isinstance(v, Field):
field_list.append(v)
cls = type.__new__(mcs, name, bases, attrs)
cls.fields = field_list
return cls
class Form(object):
__metaclass__ = Meta
class MyForm(Form):
fe1 = Field("Field1", "Vars1")
fe2 = Field("Field2", "Vars2")
x = "This won't appear"
form_fields = MyForm.fields
print(form_fields)
There are many questions on here about Python metaclasses (example), so I won't try to re-explain the concept.
In this case, when you create the class MyForm, each of the class attributes are checked for being instances of Field. If they are, they're added to a list (field_list).
The class is created, then an attribute .fields is added to the class, which is field_list, the list of Field elements.
You can then access the form fields through <FormSubclass>.fields or in the case of this example, MyForm.fields.
Edit:
It's worth noting that you can accomplish very similar functionality, without the metaclass syntactic sugar with something like:
class Field(object):
def __init__(self, *args):
self.args = args
def __repr__(self):
return "Form(%s)" % (', '.join(map(repr, self.args)),)
class Form(object):
def __init__(self):
self._fields = None
def fields(self):
if self._fields is None:
field_list = []
for k in dir(self):
v = getattr(self, k)
if isinstance(v, Field):
field_list.append(v)
self._fields = field_list
return self._fields
class MyForm(Form):
def __init__(self):
Form.__init__(self)
self.fe1 = Field("Field1", "Vars1")
self.fe2 = Field("Field2", "Vars2")
self.x = "This won't appear"
form_fields = MyForm().fields()
print(form_fields) # [Form('Field1', 'Vars1'), Form('Field2', 'Vars2')]
Short answer : you dont access subclasse's attributes from a parent class - because the parent class can not know what attributes a child class might have.
Long answer : ... unless the parent class defines a protocol allowing subclasses to let the parent class knows about at least part of it's own attributes.
Django's form framework (as well as django's orm FWIW) use such a protocol: the base Form class has a custom metaclass that collects the form.fields declared in a subclass - and do quite some black magic. FWIW, Django is oss so you could have answered the question yourself just reading the source code: https://github.com/django/django/blob/master/django/forms/forms.py
You need to refer to self.x to access Child class variables:
class Parent(object):
def __init__(self):
print(self.x)
class Child(Parent):
x = 1
if __name__ == '__main__':
child_instance = Child()
This might not help you in regards to Django Forms, but another alternative is to work with abstract classes. You would exchange attributes with methods/properties. It also prevents you from using the parent class by itself.
from abc import ABC, abstractmethod
class Parent(ABC):
#property
#abstractmethod
def x(self):
pass
def __init__(self):
print(self.x)
class Child(Parent):
#property
def x(self):
return 1
if __name__ == '__main__':
child_instance = Child() # prints "1"
parent_instance = Parent() # fails
Well, if I got you right... Maybe you're thinking of getting a field from the child class to work on the parent class. Well, that's polymorphism and it's done by overriding the parent class.
Let's assume you have :
A parent has x, now to increase x from the child and make it reflect in the parent, check the code below to get it.
class Parent:
def __init__(self, x):
self.x = x
def Print(self):
print(f"{self.x}")
class Child(Parent):
def __init__(self, x):
Parent.__init__(self, x)
x += 1
self.x = x
""""""
c1 = Child(2)
c1.Print()
#output: 3
""""""
c2 = Child(8)
c2.Print()
#output: 9

Alternative for inheritance in python

How to save code duplication in the following scenario ?
say Aand B are two classes having a common function(say) name
class A(object):
name = 'foo'
#property
def name(self): # the common function
return self.name
similarly B
class B(object):
name = 'bar'
#property
def name(self):
return self.name
One way would be to make a class from which both of them inherit from, and define name there.
Any good alternatives ?
If you're really determined to avoid inheritance, just define a function outside of either class:
def get_name(object):
return object.name
class A(object):
name = 'foo'
def get_name(self): # the common function
return self.name
class B(A):
pass
In this case B would inherit from A
Is there a reason you can't have B inherit from A?
class B(A):
name = 'bar'
Since you are decorating name with #property, I am assuming you want this to be an instance variable. If you want this to return a more private variable, let's call it _name, you have to do:
class A(object):
def __init__(self):
self._name = 'foo'
#property
def name(self):
return self._name
You can't have both a variable and a function have the same name, since the latter will simply override the former. If you want a base class that takes care of this, it would look like this:
class HasName(object):
def __init__(self, name):
self._name = name
#property
def name(self):
return self._name
class A(HasName):
def __init__(self):
self._name = 'foo'
class B(HasName):
def __init__(self):
self._name = 'bar'
You can also call the constructor in HasName.
Assuming self.name stands in for a more complex method, the easiest way to cut down on duplicated code is to move the function out to the module and have it take an arbitrary object as a parameter. Then, if you still want to tie the method directly to the class, you can add a short method that dispatches to the module function.
def _name(obj):
return obj.name
class A(object):
# ...
#property
def name(self):
return _name(self)
class B(object):
# ...
#property
def name(self):
return _name(self)
Note that this will not work well if A.name and B.name have completely different behaviors. If the _name function starts checking the type of the object given, reconsider whether you really want to abstract that functionality in the first place.

Dynamically mixin a base class to an instance in Python

Is it possible to add a base class to an object instance (not a class!) at runtime? Something along the lines of how Object#extend works in Ruby:
class Gentleman(object):
def introduce_self(self):
return "Hello, my name is %s" % self.name
class Person(object):
def __init__(self, name):
self.name = name
p = Person("John")
# how to implement this method?
extend(p, Gentleman)
p.introduce_self() # => "Hello, my name is John"
This dynamically defines a new class GentlePerson, and reassigns p's class to it:
class Gentleman(object):
def introduce_self(self):
return "Hello, my name is %s" % self.name
class Person(object):
def __init__(self, name):
self.name = name
p = Person("John")
p.__class__ = type('GentlePerson',(Person,Gentleman),{})
print(p.introduce_self())
# "Hello, my name is John"
Per your request, this modifies p's bases, but does not alter p's original class Person. Thus, other instances of Person are unaffected (and would raise an AttributeError if introduce_self were called).
Although it was not directly asked in the question, I'll add for googlers and curiosity seekers, that it is also possible to dynamically change a class's bases but (AFAIK) only if the class does not inherit directly from object:
class Gentleman(object):
def introduce_self(self):
return "Hello, my name is %s" % self.name
class Base(object):pass
class Person(Base):
def __init__(self, name):
self.name = name
p = Person("John")
Person.__bases__=(Gentleman,object,)
print(p.introduce_self())
# "Hello, my name is John"
q = Person("Pete")
print(q.introduce_self())
# Hello, my name is Pete
Slightly cleaner version:
def extend_instance(obj, cls):
"""Apply mixins to a class instance after creation"""
base_cls = obj.__class__
base_cls_name = obj.__class__.__name__
obj.__class__ = type(base_cls_name, (base_cls, cls),{})
Although it's already answered, here is a function:
def extend(instance, new_class):
instance.__class__ = type(
'%s_extended_with_%s' % (instance.__class__.__name__, new_class.__name__),
(instance.__class__, new_class),
{},
)

Categories

Resources