Class Attributes and Instance Attributes - python

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.

Related

Python Inheritence from constructor

person.py
class Person:
"""---A class representing a person---"""
# Person constructor
def __init__(self,n,a):
self.full_name = n
self.age = a
class Student(Person):
# Student constructor
def __init__(self,n,a,s):
Person.__init__(self,n,a)
self.school = s
driver.py
from person import *
a = Student("Alice", 19, "Univ")
It throws TypeError: __init__() takes 3 positional arguments but 4 were given
I tried to change Student class to the following:
class Student(Person):
# Student constructor
def __init__(self,n,a,s):
super().__init__(n,a)
self.school = s
The error still exists.
Why does this happen? Is super() keyword required to add new attributes?
EDIT: The problem is solved. There was an indentation issue in the source code rendering this strange behavior, hence the question should be closed.
This line:
Person.__init__(self,n,a)
Is the problem. Recall that methods are automatically passed a reference to themselves, so you just passed a second one.
There's also a well-established pattern for this:
class Person
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, school, *args):
super().__init__(*args)
self.school = school
student = Student('Washington Elementary', "Johnny Go'gettem", 10)
although note that simply removing your reference to self in the Person.__init__ call inside Student.__init__ would be sufficient.
Note that you can override the default method behavior with a couple of decorators that become quite useful in certain situations. Neither apply here, but just a bit of knowledge to tease your brain a bit:
def SomeClass:
attr = "class-scoped"
def __init__(self):
self.attr = "instance-scoped"
def some_method(self):
return self.attr == "instance-scoped"
#classmethod
def some_classmethod(cls):
return cls.attr == "class-scoped"
#staticmethod
def some_staticmethod():
return "I'm not given a \"self\" parameter at all!"
classmethods are particularly useful as alternate constructors
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
#classmethod
def from_tuple(cls, tup) -> "Person":
"""Expects a tuple of (name, age) and constructs a Person"""
name, age = tup
return cls(name, age)
#classmethod
def from_dict(cls, dct) -> "Person":
"""Expects a dictionary with keys "name" and "age" and constructs a Person"""
try:
name = dct['name']
age = dct['age']
except KeyError:
raise ValueError(f"Dictionary {dct} does not have required keys 'name' and 'age'")
else:
return cls(name, age)

Using class method to access class variable within a class

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

Listing instance attributes with __dict__

Hi I am trying to use composition to create a new class using instances of another class when I try to turn the new object into a dictionary using __dict__, it's shows me <__main__.myobjec object at 0x00000000029CA908>, not sure am I using the __dict__ incorrectly though I have heard its related to new classes, any help greatly appreciated.
class User:
def __init__(self, name, job=None):
self.name = name
self.job = job
class Customer(User):
_ID = 100
def __init__(self, name, job=None):
self.name = name
self.job = job
class Account:
_ID = 0
def __init__(self, name, job=None):
self.customer = Customer(name , "Customer")
def __getattr__(self, attr):
return getattr(self.customer, attr)
>>> A = Account("Abdi")
>>> A.__dict__
{'customer': <__main__.Customer object at 0x109fdbfc8>}
>>>
You need to implement the __repr__ method to represent all of the instances of the Customer class.
def __repr__(self): return repr(self.__dict__) # the dictionary of attributes in __repr__

Add an object of one class to an object of another class in python

I have a class Participant as follows:
class Participant:
def __init__(self,name,level):
self.name = name
self.level =level
I also have another class Team as follows:
class Team:
def __init__(self,name):
self.name = name
I want to create a method in the class Team to add an instance of class Participant; for instance:
Assume myTeam is an empty, valid instance of Team.
Also assume myParticipant1 is a valid instances of Participant
myTeam.addParticipant(participant= myParticipant1)
AddParticipant method should add the instance myParticipant1 to the instance myTeam.
How do I acheive it in Python?
Aside from the inheritance questions we're talking about in the comments, this is pretty simple stuff.
class Participant(object):
def __init__(self, name, level):
self.name = name
self.level = level
class Team(object):
def __init__(self, name):
self.name = name
self.participants = []
def add_participant(p):
self.participants.append(p)
DEMO:
my_team = Team("Monty Python")
p_info = [("Adam", 10e5), ("Joe-bob", -1)]
participants = [Participant(name, level) for name, level in p_info]
for participant in participants:
my_team.add_participant(participant)
# say that 10 times fast....
In [1]: [p.name for p in my_team.participants]
Out[1]: ["Adam", "Joe-bob"]

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