If I understand your question correctly, you should be able to reference class A within class A by putting the type annotation in quotes. This is called forward reference.
class A:
# do something
def some_func(self, a: 'A')
# ...
See ref below
- https://github.com/python/mypy/issues/3661
- https://www.youtube.com/watch?v=AJsrxBkV3kc
If I understand your question correctly, you should be able to reference class A within class A by putting the type annotation in quotes. This is called forward reference.
class A:
# do something
def some_func(self, a: 'A')
# ...
See ref below
- https://github.com/python/mypy/issues/3661
- https://www.youtube.com/watch?v=AJsrxBkV3kc
In Python you cannot reference the class in the class body, although in languages like Ruby you can do it.
In Python instead you can use a class decorator but that will be called once the class has initialized. Another way could be to use metaclass but it depends on what you are trying to achieve.
Before I go into answering your question, I want to mention some things about terminology. Your myclass value is an instance, not a class. You do have a class in your code, named AddSub. When you called AddSub() you created an instance of that class. It's important to learn the right terminology for things like this, so you can ask good questions and understand the answers you get back.
Your code comes close to working because you're saving an instance of the AddSub class to a global variable named myclass. Later, you call some methods on that global variable from the try_block function. This is legal in Python, though generally not recommended.
Instead, you should pass the object as an argument:
def try_block(value):
try:
value.set_x(whatever())
except ValueError:
pass
You'd call it by passing an instance of your AddSub class to the function:
myAddSub = AddSub() # create the instance
try_block(myAddSub) # pass it to the function
This is much nicer because it doesn't depend on a global variable having a specific value to work and you can call it with many different AddSub instances, if you want.
One part of your code that's currently broken is the AddSub class's constructor. There's no need to declare variables. You can just assign to them whenever you want. If you want to have default values set, you can do that too:
def __init__(self):
self._x = 0
self._y = 0
If instead you want to be able to set the values when you construct the object, you can add additional parameters to the __init__ method. They can have default values too, allowing the caller to omit some or all of them:
def __init__(self, x=0, y=0):
self._x = x
self._y = y
With that definition, all of these will be valid ways to construct an AddSub instance:
a = AddSub() # gets default 0 value for both _x and _y
b = AddSub(5) # gets default 0 value for _y
c = AddSub(y=5) # gets default 0 value for _x
d = AddSub(2, 3) # no defaults!
Finally a point that is mostly independent of your main question: Your try_block function is both poorly named, and implemented in a more complicated way than necessary. Instead of being recursive, I think it would make more sense as a loop, like in this psuedocode version:
def exception_prone_task():
while True: # loops forever, or until a "return" or "break" statement happens
try:
result = do_stuff_that_might_raise_an_exception()
return result # you only get here if no exception happened
except WhateverExceptions as e:
report_about_about_the_exceptions(e)
To answer the question posed in the title (in case that is still bringing people to this question):
Python objects are passed in and out of functions by pointer value. That's a lot like being passed by reference, but it's not exactly the same as how references work in other languages (like C++). You can mutate a value being passed in, as long as you do it in-place. You cannot, however, rebind the value of the object and see the same rebinding outside the function.
Here are some examples that demonstrate the differences:
value = [1, 2, 3]
def foo(x):
x.append(4) # mutate x in place
print('inside', x)
foo(value) # prints 'inside [1, 2, 3, 4]'
print('outside', value) # prints 'outside [1, 2, 3, 4]'
def bar(x):
x = [9, 8, 7] # this rebinding is allowed, but may not do what you want
print('inside', x)
bar(outside_value) # prints 'inside [9, 8, 7]'
print('outside', value) # still prints 'outside [1, 2, 3, 4]'
Short answer: everything in Python is passed by reference. (Some people like to add "by reference value").
Longer answer is that your code is a bit confusing. (No problem, it can be improved.)
Firstly, the myclass should not be called that way, because it is not a class. It is an instance of the AddSub class -- i.e. the object of that class.
Secondly, classes should not be given names of the verb. They should be nouns.
The first argument of each class method should be self (for simplicity). The next arguments are the ones that were passed when the class is created (i.e. when the class name is used as if you were calling a function).
Whenever you want to give the argument a default value, you just write =value just after the argument.
Everything inside the method definitions that is part of the future object, must be prefixed by self. (simplified). This way, your __init__ should look like:
def _init_(self, x=1, y=2): #how do default parameters work?
self._x = x
self._y = y
In my opinion, it is not neccessary to use the _ (underscore) prefix for the object variables.
That reference isn't describing a special case of the language rules, it's a natural result of everything being an object.
The special case is that MyClass(args...) is wired up to create a new object and call MyClass.__init__ (among other things).
It allows you to change the state of the class after it is defined. This can be as simple as changing "static" data based on some configuration.
class EggsApiClient:
default_url = "example.com"
def connect(self):
# do stuff with default_url
if __name__ = "main":
EggsApiClient.default_url = parse_args("default_url")
This is not something that I think most Python users will ever need to do and almost surely shouldn't but it does have a distinct purpose and it's worth understanding.
Consider the following example:
class Foo:
def set_a(self, a):
self.a = a
class Bar:
def set_b(self, b):
self.b = b
foo = Foo()
foo.set_a(1)
print(foo.a)
bar = Bar()
# interesting part here!
Foo.set_a(bar, 2)
print(bar.a)
Through this feature, we have effectively called set_a on a Bar instance, despite the fact that Bar doesn't have a set_a method defined. That is, if we try:
bar.set_a()
We get an error: AttributeError: 'Bar' object has no attribute 'set_a'.
You might (quite reasonably) ask, "OK, but why would I do that?" Personally, I think I did something like this many moons ago but there was probably a simpler solution to whatever I was trying to accomplish.
I can imagine this be useful in various frameworks e.g. something like unit testing. But the main reason I think you might end up doing this is when you are using a more functional style. For example, you might have a function like this which has no knowledge of Foo or Bar:
def apply(obj, func, *params):
func(obj, *params)
Which you can then call like so:
apply(bar, Bar.set_b, 3)
print(bar.b)
A real example of the kind of thing you might actually do:
class Person:
def __init__(self, name: str, student: bool):
self.name = name
self.student = student
def is_student(self):
return self.student
def __repr__(self):
return f"{self.name} - student: {self.student}"
people = [Person("bob", False), Person("alice", True), Person("carl", True)]
def display(iter):
print("---")
for i in iter:
print(i)
display(people)
display(filter(Person.is_student, people))
Or maybe something like this:
people = map(Person, ["dave", "edith", "frank"], [True, False, True])
display(people)
Check out the functools module for more interesting functional-style approaches like this.
Try:
class Person(object):
def __init__(self, fn, ln, address):
self.uid = Id_Class.new_id("Person")
self.f_name = fn
self.l_name = ln
self.address = address
class Address(object):
def __init__(self, st, sub):
self.uid = Id_Class.new_id("Address")
self.street = st
self.suburb = sub
hm = Address('Queen St.', 'Sydney')
s = Person('John', 'Doe', hm)
However you want. Perhaps the simplest way is:
s.address = hm
But you don't say what you mean by "attach". If you want something more elaborate (e.g., if you want the address to be created when the Person object is created) then you'll need to explain more.
Assignment statements in Python don't copy objects, they create bindings between the target and an object.
An easy to understand guide on how this different from other languages is available here.
When you need to change an assigned object so it does not affect other assigned objects you can use the copy module. (https://docs.python.org/3.4/library/copy.html)
import copy
class A:
def __init__(self):
self.x = 1
def changeX(self,num):
self.x = num
class B:
def __init__(self,classA):
self.x = classA
class C:
def __init__(self,classA):
self.x = classA
def ChangeA(self,num):
self.x.changeX(num)
c_a = A()
c_b = B(c_a)
c_c = copy.deepcopy(C(c_a))
c_c.ChangeA(2)
c_a.x #1
c_b.x.x #1
c_c.x.x #2
Unless you create a copy Python won't create a copy for you. You can use the copy module to create copies of objects or you can create a new object with the same values.
In your example there is only once instance of class A and each of the c_a, c_b and c_c have access to it. That's why you're seeing 2 in each case as they're all the same attribute on the same object.
Whatever is associated with a variable name has to be stored in the program's memory somewhere. An easy way to think of this, is that every byte of memory has an index-number. For simplicity's sake, lets imagine a simple computer, these index-numbers go from 0 (the first byte), upwards to however many bytes there are.
Say we have a sequence of 37 bytes, that a human might interpret as some words:
"The Owl and the Pussy-cat went to sea"
The computer is storing them in a contiguous block, starting at some index-position in memory. This index-position is most often called an "address". Obviously this address is absolutely just a number, the byte-number of the memory these letters are residing in.
@12000 The Owl and the Pussy-cat went to sea
So at address 12000 is a T, at 12001 an h, 12002 an e ... up to the last a at 12037.
I am labouring the point here because it's fundamental to every programming language. That 12000 is the "address" of this string. It's also a "reference" to it's location. For most intents and purposes an address is a pointer is a reference. Different languages have differing syntactic handling of these, but essentially they're the same thing - dealing with a block of data at a given number.
Python and Java try to hide this addressing as much as possible, where languages like C are quite happy to expose pointers for exactly what they are.
The take-away from this, is that an object reference is the number of where the data is stored in memory. (As is a pointer.)
Now, most programming languages distinguish between simple types: characters and numbers, and complex types: strings, lists and other compound-types. This is where the reference to an object makes a difference.
So when performing operations on simple types, they are independent, they each have their own memory for storage. Imagine the following sequence in python:
>>> a = 3
>>> b = a
>>> b
3
>>> b = 4
>>> b
4
>>> a
3 # <-- original has not changed
The variables a and b do not share the memory where their values are stored. But with a complex type:
>>> s = [ 1, 2, 3 ]
>>> t = s
>>> t
[1, 2, 3]
>>> t[1] = 8
>>> t
[1, 8, 3]
>>> s
[1, 8, 3] # <-- original HAS changed
We assigned t to be s, but obviously in this case t is s - they share the same memory. Wait, what! Here we have found out that both s and t are a reference to the same object - they simply share (point to) the same address in memory.
One place Python differs from other languages is that it considers strings as a simple type, and these are independent, so they behave like numbers:
>>> j = 'Pussycat'
>>> k = j
>>> k
'Pussycat'
>>> k = 'Owl'
>>> j
'Pussycat' # <-- Original has not changed
Whereas in C strings are definitely handled as complex types, and would behave like the Python list example.
The upshot of all this, is that when objects that are handled by reference are modified, all references-to this object "see" the change. So if the object is passed to a function that modifies it (i.e.: the content of memory holding the data is changed), the change is reflected outside that function too.
But if a simple type is changed, or passed to a function, it is copied to the function, so the changes are not seen in the original.
For example:
def fnA( my_list ):
my_list.append( 'A' )
a_list = [ 'B' ]
fnA( a_list )
print( str( a_list ) )
['B', 'A'] # <-- a_list was changed inside the function
But:
def fnB( number ):
number += 1
x = 3
fnB( x )
print( x )
3 # <-- x was NOT changed inside the function
So keeping in mind that the memory of "objects" that are used by reference is shared by all copies, and memory of simple types is not, it's fairly obvious that the two types operate differently.
Objects are things. Generally, they're what you see on the right hand side of an equation.
Variable names (often just called "names") are references to the actual object. When a name is on the right hand side of an equation1, the object that it references is automatically looked up and used in the equation. The result of the expression on the right hand side is an object. The name on the left hand side of the equation becomes a reference to this (possibly new) object.
Note, you can have object references that aren't explicit names if you are working with container objects (like lists or dictionaries):
a = [] # the name a is a reference to a list.
a.append(12345) # the container list holds a reference to an integer object
In a similar way, multiple names can refer to the same object:
a = []
b = a
We can demonstrate that they are the same object by looking at the id of a and b and noting that they are the same. Or, we can look at the "side-effects" of mutating the object referenced by a or b (if we mutate one, we mutate both because they reference the same object).
a.append(1)
print a, b # look mom, both are [1]!
1More accurately, when a name is used in an expression