self variable in Python vs class variable - python

Can you please clarify about the perimeter variable in the below class.
I understand that the self.vertices is to a particular instance. Since perimeter is not defined with self, does that mean its a class variable here? Then is it not common to all the instances?
Is it not the right way to code the perimeter as self.perimeter, so its aptly declared to each instance?
This code is from a book.
Polygon.py
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, p2):
return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
class Polygon:
def __init__(self):
self.vertices = []
def add_point(self, point):
self.vertices.append((point))
def perimeter(self):
perimeter = 0
points = self.vertices + [self.vertices[0]]
for i in range(len(self.vertices)):
perimeter += points[i].distance(points[i+1])
return perimeter
>>> square = Polygon()
>>> square.add_point(Point(1,1))
>>> square.add_point(Point(1,2))
>>> square.add_point(Point(2,2))
>>> square.add_point(Point(2,1))
>>> square.perimeter()
4.0
New type
import math
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def distance(self, p2):
return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
class Polygon:
def __init__(self):
self.vertices = []
def add_point(self, point):
self.vertices.append((point))
def perimetermethod(self):
self.perimeter = 0
points = self.vertices + [self.vertices[0]]
for i in range(len(self.vertices)):
self.perimeter += points[i].distance(points[i+1])
return self.perimeter
if __name__=='__main__':
p1 = Polygon()
p1.add_point(Point(1,1))
p1.add_point(Point(1,2))
p1.add_point(Point(2,2))
p1.add_point(Point(2,1))
print(p1.perimetermethod())

Assigning a new variable with some_name = ... always creates the variable in the innermost enclosing scope (unless global or nonlocal are in play, but they're not relevant here). Assigning a new attribute name on an object creates the attribute on that object.
So self.foo = 1 assigns an attribute named foo on the object currently referred to by self. Conventionally, the name self is used as the first parameter to a method, which receives the object on which the method was invoked. So "defining a variable with self" isn't anything special; it's just the ordinary rules about assigning to attributes on existing object. Any attributes that exist in instance object itself obviously have to be specific to that instance.
perimeter = 0 inside the perimeter method of the Polygon class creates a variable in the innermost enclosing scope. That's the perimeter method, so it creates a local variable. A local variable only exists for the duration of the function call, so it's neither a class variable nor an instance variable. You can't access it from anywhere except within the scope of that particular method (and it has a new completely independent value on each invocation), so it can't be common to all the instances. But neither can you access a different value for it on each particular instance, so it's not an instance variable either.
If you had perimeter = 0 outside a method, in the class block itself, then the innermost enclosing scope would be the class block. That would create a "class variable", which is just an attribute on the class object. If an attribute is on a class, then obviously it can't be specific to any instance, because there's only one class but there can be any number of instances. As an aside, this is exactly what the __init__, add_point, and perimeter methods of the Polygon class are; they were assigned (with a def statement) in a class block, so they became attributes of the class object.
Summary:
self.foo = 1 is assigning to an attribute on the object currently referenced by self (this is usually the "current instance")
foo = 1 in a class block is creating a class attribute of the class being defined
foo = 1 in a def block is creating a local variable of the function being defined
But you shouldn't really memorise it that way. They're just special cases of:
Assigning to a dotted name like foo.bar.baz = 1 is writing to an attribute of an object
Assigning to a simple name like foo = 1 is writing to a variable in the innermost enclosing scope

No, that means that it's local. Also, you don't want to use self.perimeter since it will shadow the method with the same name.

In your code, there are two things called perimeter. One is a method on the Polygon class. The other is a local variable inside that method. There are no class attributes in your code.

Related

how can fix this Python Code is not defined

surface=self.surface
NameError: name 'self' is not defined
how can fix this Python Code is not defined
the code
class Rectangle:
def __init__(self, longueur=30, largeur=15):
self.lon = longueur
self.lar = largeur
self.nom = "rectangle"
def surface(self):
return self.lon * self.lar
surface=self.surface
def affichage(self):
print("rectangle=" + self.nom, "longueur=" + self.lon, "largeur=" + self.lar, "surface=" + self.surface)
class Carre(Rectangle):
def __init__(self, cote=10):
Rectangle.__init__(self, cote, cote)
self.nom = "carre"
r = Rectangle()
print(r)
c = Carre()
print(c)
At this line surface=self.surface you're trying to access a variable that does not exist in this scope. self has only been defined within the context of the various functions of your classes, and python doesn't know about it outside of those functions.
If you have an instance of Rectangle called for example rect, you can refer to its member function surface as rect.surface, or you can evaluate that function's value by calling rect.surface().
The key to understanding this is to know that objects can have many names. By convention, within the object we refer to the instance by the name self. Outside of the object, this would be confusing so we use names that tell us what object we're referring to. (Just as you might refer to yourself as "me", but you'd be confused if others used that same word to refer to you!)

How can I access the changed variable from another class

I want to print(read or use) the changing variable self.tt in B().check() while class A is changing self.tt, is it any way to do it or other better solution to achieve the task?
class A:
def __init__(self):
self.tt = 0
def change_tt(self):
for i in range(100000000):
self.tt += 1
sleep(1)
class B():
def __init__(self):
self.x = A.tt
def check(self):
while True:
print(self.x)
a = A()
b = B()
x1 = threading.Thread(target=A.change_tt)
x2 = threading.Thread(target=B.check)
x1.start()
x2.start()
AttributeError: type object 'A' has no attribute 'tt'
There are a couple of things going on here. The biggest issue is that you can't access an instance attribute from the class. A secondary issue is that integers are immutable.
Here is some code that should fix both:
class A:
def __init__(self):
self.tt = 0
def change_tt(self):
for i in range(100000000):
self.tt += 1
sleep(1)
class B():
def __init__(self, a):
self.a = a
def check(self):
while True:
print(self.a.tt)
a = A()
b = B(a)
x1 = threading.Thread(target=a.change_tt)
x2 = threading.Thread(target=b.check)
x1.start()
x2.start()
Notice that the code of class A remains unchanged. However, it's important to understand that it is being used differently. A is the class object. It has attributes that are functions, but no integers. When you create instance a, the functions become methods when you access them with the . operator. a also has an attribute tt.
A thread target should be a no-arg callable. The functions A.change_tt and B.check both require a single positional argument, self. However, the bound methods a.change_tt and b.check are no-arg callables. The process of binding a function to an instance with the . operator creates a wrapper that passes in self automatically.
When you do self.tt += 1, the object that is the previous value of tt is unbound from tt and possibly garbage collected. Integers are immutable, which means that what really happens here is self.tt = self.tt + 1. That means that the statement self.x = A.tt in B.__init__ is unreasonable even if tt existed in A. x would be a reference to the object that is the initial value of tt, and would keep referring to that object even as tt changed to the incremented version.
An instance of B needs to know about the object that A.tt refers to currently. One way to do that is to pass a reference to B.__init__. That's why we define __init__(self, a), and invoke B as B(a) to get an instance that refers to a.

OOP: Init method questions

I have two questions regarding the code below.
What is the difference between self.a=self.test1() and a=self.test1()? One is class field and other one is object field?
Why cannot I define result = self.a+self.b? How to correct it?
class Test():
def __init__(self):
self.a=self.test1()
a=self.test1()
self.b=Test.test2()
result = self.a+self.b
def test1(self):
a=100
return a
#classmethod
def test2(cls):
b=200
return b
#staticmethod
def test3():
print("Testing3 is calling ")
c=500
return c
self.a = self.test1() creates an instance attribute named a. The attribute will be accessible from the object anywhere you have a reference to the object.
a = self.test1() defines a local variable named a. It will go out of scope once __init__ returns.
result = self.a + self.b doesn't work because it is in a context where self is not defined. self is just the (conventional) name of the first parameter of an instance method. It's not defined in the namespace of the class statement itself.
self.a is a property in this class. It will remain accessible throughout functions in the Test() class. a = self.test1(), however, goes away once __init__(self) finishes, because a is local to __init__(self).
For result = self.a + self.b, I assume you want a variable called result calculated after self.a and self.b is defined? At that indentation level a statement like this is usually not allowed (I could be wrong), usually a declaration of a property of a class happens here.

Find Static, instance and local variables in this python code

Here is the question of a python code asked on InfyTQ mock test.
class classOne:
__var_one = 1001
def __init__(self,var_two):
self.__var_two = var_two
self.__var_five = 5
def method_one(self):
var_four = 50
self.__var_five = ClassOne.__var_one + self.__var_two + var_four
Now, I want to ask if the variable
self.__var_five of function method_one should be considered a new instance variable or not?
Because there is already a self.__var_five in __init__ function.
Also,
I learned the concept of global,local,static and instance variable from given below code.
Is it correct?
#global, local, static, instance variable.
#global variable are defined at the top of program or defined using keyword:global
global global_var1 = 0
global_var2 = 1
def local_variable:
#local variable are defined inside of a function.
local_var1 = 2
class static_instance:
#static/classs variable are defined inside of a class.
static_var1 = 3
def __init__(self):
#all variables defined in the function of a class starting with self.
self.instance_var1 = 4
def static(self):
self.instance_var2 = 5
local_var2 = 6 #local variable as it is in a function.
static_var2 = 6
It's the same instance variable (called an attribute in Python). method_one is simply updating its value.
Most of your understandings in the second code block are correct. However, when a method does:
self.static_var1 = 4
it creates an instance attribute named static_var1. This is independent of the class attribute that was declared outside the methods, which is shared among all instances that don't reassign it. But since you do the assignment in the __init__() method, all instances get their own attribute. The only way to access the static value would be with static_instance.static_var1.

Python unittest.main() unable to instantiate inner classes [duplicate]

I'm trying to understand scope in nested classes in Python. Here is my example code:
class OuterClass:
outer_var = 1
class InnerClass:
inner_var = outer_var
The creation of class does not complete and I get the error:
<type 'exceptions.NameError'>: name 'outer_var' is not defined
Trying inner_var = Outerclass.outer_var doesn't work.
I get:
<type 'exceptions.NameError'>: name 'OuterClass' is not defined
I am trying to access the static outer_var from InnerClass.
Is there a way to do this?
class Outer(object):
outer_var = 1
class Inner(object):
#property
def inner_var(self):
return Outer.outer_var
This isn't quite the same as similar things work in other languages, and uses global lookup instead of scoping the access to outer_var. (If you change what object the name Outer is bound to, then this code will use that object the next time it is executed.)
If you instead want all Inner objects to have a reference to an Outer because outer_var is really an instance attribute:
class Outer(object):
def __init__(self):
self.outer_var = 1
def get_inner(self):
return self.Inner(self)
# "self.Inner" is because Inner is a class attribute of this class
# "Outer.Inner" would also work, or move Inner to global scope
# and then just use "Inner"
class Inner(object):
def __init__(self, outer):
self.outer = outer
#property
def inner_var(self):
return self.outer.outer_var
Note that nesting classes is somewhat uncommon in Python, and doesn't automatically imply any sort of special relationship between the classes. You're better off not nesting. (You can still set a class attribute on Outer to Inner, if you want.)
I think you can simply do:
class OuterClass:
outer_var = 1
class InnerClass:
pass
InnerClass.inner_var = outer_var
The problem you encountered is due to this:
A block is a piece of Python program text that is executed as a unit.
The following are blocks: a module, a function body, and a class
definition.
(...)
A scope defines the visibility of a name within
a block.
(...)
The scope of names defined in a class block is
limited to the class block; it does not extend to the code blocks of
methods – this includes generator expressions since they are
implemented using a function scope. This means that the following will
fail:
class A:
a = 42
b = list(a + i for i in range(10))
http://docs.python.org/reference/executionmodel.html#naming-and-binding
The above means:
a function body is a code block and a method is a function, then names defined out of the function body present in a class definition do not extend to the function body.
Paraphrasing this for your case:
a class definition is a code block, then names defined out of the inner class definition present in an outer class definition do not extend to the inner class definition.
You might be better off if you just don't use nested classes. If you must nest, try this:
x = 1
class OuterClass:
outer_var = x
class InnerClass:
inner_var = x
Or declare both classes before nesting them:
class OuterClass:
outer_var = 1
class InnerClass:
inner_var = OuterClass.outer_var
OuterClass.InnerClass = InnerClass
(After this you can del InnerClass if you need to.)
Easiest solution:
class OuterClass:
outer_var = 1
class InnerClass:
def __init__(self):
self.inner_var = OuterClass.outer_var
It requires you to be explicit, but doesn't take much effort.
In Python mutable objects are passed as reference, so you can pass a reference of the outer class to the inner class.
class OuterClass:
def __init__(self):
self.outer_var = 1
self.inner_class = OuterClass.InnerClass(self)
print('Inner variable in OuterClass = %d' % self.inner_class.inner_var)
class InnerClass:
def __init__(self, outer_class):
self.outer_class = outer_class
self.inner_var = 2
print('Outer variable in InnerClass = %d' % self.outer_class.outer_var)
All explanations can be found in Python Documentation The Python Tutorial
For your first error <type 'exceptions.NameError'>: name 'outer_var' is not defined. The explanation is:
There is no shorthand for referencing data attributes (or other methods!) from within methods. I find that this actually increases the readability of methods: there is no chance of confusing local variables and instance variables when glancing through a method.
quoted from The Python Tutorial 9.4
For your second error <type 'exceptions.NameError'>: name 'OuterClass' is not defined
When a class definition is left normally (via the end), a class object is created.
quoted from The Python Tutorial 9.3.1
So when you try inner_var = Outerclass.outer_var, the Quterclass hasn't been created yet, that's why name 'OuterClass' is not defined
A more detailed but tedious explanation for your first error:
Although classes have access to enclosing functions’ scopes, though, they do not act
as enclosing scopes to code nested within the class: Python searches enclosing functions
for referenced names, but never any enclosing classes. That is, a class is a local scope
and has access to enclosing local scopes, but it does not serve as an enclosing local scope
to further nested code.
quoted from Learning.Python(5th).Mark.Lutz
class c_outer:
def __init__(self, name:str='default_name'):
self._name = name
self._instance_lst = list()
self._x = self.c_inner()
def get_name(self):
return(self._name)
def add_inner_instance(self,name:str='default'):
self._instance_lst.append(self.c_inner(name))
def get_instance_name(self,index:int):
return(self._instance_lst[index].get_name())
class c_inner:
def __init__(self, name:str='default_name'):
self._name = name
def get_name(self):
return(self._name)
outer = c_outer("name_outer")
outer.add_inner_instance("test1")
outer.add_inner_instance("test2")
outer.add_inner_instance("test3")
inner_1 = outer.c_inner("name_inner1")
inner_2 = outer.c_inner("name_inner2")
inner_3 = outer.c_inner("name_inner3")
print(outer.get_instance_name(index=0))
print(outer.get_instance_name(1))
print(outer._instance_lst[2]._name
print(outer.get_name())
print(inner_1.get_name())
print(inner_2.get_name())
test1
test2
test3
name_outer
name_inner1
name_inner2
name_inner3

Categories