Pythonic way to reference an object in another class? - python

I am trying to figure the best way to connect classes. Specifically, I want to know whether it is Pythonic to reference an object when creating a class.
So here are two examples that produce the same outcome:
Example 1
class A:
def __init__(self,content):
self.content = content
class B:
def __init__(self):
self.content = a.content
a = A('test')
b = B()
print(b.content)
Example 2
class A:
def __init__(self,content):
self.content = content
class B:
def __init__(self,other_object):
self.content = other_object.content
a = A('test')
b = B(a)
print(b.content)
In example 1 the object a is being used inside of the class. In example 2 that object is passed in as argument.
I get that example 2 is the better option because it is more deliberate, but would example 1 still be good practice?

The two are implementing two fundamentally different functionalities:
in the first approach you do not expose the object to work upon and rely on some global name to be defined.
in the second you explicitly ask for an object to use, which makes class B more self-contained.
Generally speaking, code that rely on global non-built-in names is considered bad practice.
But there are situations were this is acceptable.
For example, if a is holding an expensive computation that you do not really want to recompute each time (and even then, you could use memoization instead), then using a global name may be acceptable, but should be clearly documented.

The first code is in my mind not good practice. Because a is initialized below the definition of B. So it is very counter intuitive to read.
The best solution depends on what the underlying logic is. Example 1 would be relevant if the class A is a singleton, e.g a database. But the initialization of a should be above B in my opinion.
If you always pass the same instance then it make sense to "hardcode" the attribute in the class otherwise I would use Example 2.

Related

Why is a class variable accessable from outside

Learing Python I just encountered something I do not really understand. Let us take this example:
class CV_Test:
classVar = 'First'
cv = CV_Test()
print(cv.classVar)
CV_Test.classVar = 'Second'
cv2 = CV_Test()
print(cv2.classVar)
print(CV_Test.classVar)
Output:
First
Second
Second
Can anyone tell me why this is possible and what it is good for? Isn't this contradictory to defining a class as a blueprint if I can change maybe crucial values within a class from outside and is this not a conflict of the OOP paradigam of encapsulation. Coming from .NET I actually just know accessing variables via a getter and setter but not just like this. So I am curious what important purpose there can be that this is allowed.
Why is it possible? Python does not follow a restrictive programming paradigm, meaning that if something can make sense in some scenario, the interpreter should not stand in the way of the programmer willing to do that.
That being said, this approach requires a higher level of discipline and responsibility on the programmer's side, but also allows for a greater degree of flexibility in its meta-programming capabilities.
So, in the end this is a design choice. The advantage of it is that you do not need to explicitly have to use getters/setters.
For protected/private members/methods it is customary to prepend a _ or __, respectively. Additionally, one would be able to fake a getter/setter protected behavior (which would also allow the execution of additional code) via the method decorators #property and #.setter, e.g.:
class MyClass():
_an_attribute = False
#property
def an_attribute(self):
return self._an_attribute
#an_attribute.setter
def an_attribute(self, value):
self._an_attribute = value
This can be used like this:
x = MyClass()
x.an_attribute
# False
x.an_attribute = 1
# sets the internal `_an_attribute` to 1.
x.an_attribute
# 1
and you can leave out the #an_attribute.setter part, if you want a read-only (sort of) property, so that the following code:
x = MyClass()
x.an_attribute
# False
but, attempting to change its value would result in:
x.an_attribute = 1
AttributeError: can't set attribute
Of course you can still do:
x._an_attribute = 2
x.an_attribute
# 2
(EDIT: added some more code to better show the usage)
EDIT: On monkey patching
Additionally, in your code, you are also modifying the class after its definition, and the changes have retrospective (sort of) effects.
This is typically called monkey patching and can again be useful in some scenarios where you want to trigger a certain behavior in some portion of code while keeping most of its logic, e.g.:
class Number():
value = '0'
def numerify(self):
return float(self.value)
x = Number()
x.numerify()
# 0.0
Number.numerify = lambda self: int(self.value)
x.numerify()
# 0
But this is certainly not a encouraged programming style if cleaner options are available.

Python Inherited Classes all return same random number?

I have a few classes with almost identical contents, so I tried two methods to copy the classes and their attributes over. The classes copy correctly, but the randint function is only invoked in the main class, so the same number is output every time. Is there any way to recalculate the random number for each class?
class a:
exampleData = random.randint(1,100)
b = type('b', a.__bases__, dict(a.__dict__))
class c(a):
pass
For example if a.exampleData = 50, b.exampleData and c.exampleData would be the same. Is there any way around this?
Edit -- Part of my program displays characters with random stats each time, and the class contains the stats associated with each character. The random numbers pick the stats out of a list, but the same stats are being chosen, instead of being random in each class. I may not be explaining this right, so basically:
data = [stat1,stat2,stat3,ect,,]
data[random.randint(1,3)]
When you write this:
b = type('b', a.__bases__, dict(a.__dict__))
… you're just copying a.__dict__. Since a.__dict__ is just {'exampleData': 50}, the new copy that ends up as b.__dict__ is also going to be {'exampleData': 50}.
There are many ways you could get a new random number. The simplest is to just create a new random number for b explicitly:
bdict = dict(a.__dict__)
b['exampleData'] = random.randint(1,100)
b = type('b', a.__bases__, bdict)
If you want to create a bunch of classes this way, you can wrap that up in a function:
def make_clone(proto, name):
clonedict = dict(proto.__dict__)
clonedict['exampleData'] = random.randint(1,100)
return type(name, proto.__bases__, clonedict)
You can make that factory function more complicated if you want to be (see namedtuple for a pretty extreme example).
You could wrap that behavior up in a decorator:
def randomize(cls):
cls.exampleData = random.randint(1,100)
#randomize
class a:
pass
b = randomize(type('b', a.__bases__, dict(a.__dict__)))
Notice that I had to call the decorator with normal function-call syntax here, because there's no declaration statement to attach an #decorator to.
Or you can wrap it up in a metaclass:
class RandomMeta(type):
def __new__(mcls, name, bases, namespace):
d = dict(namespace)
d['exampleData'] = random.randint(1,100)
return type.__new__(mcls, name, bases, d)
class a(metaclass=RandomMeta):
pass
b = type(a)('b', a.__bases__, dict(a.__dict__))
Notice that we have to call type(a) here, the same way a class definition statement does, not the base metaclass type.
Also notice that I'm not taking **kwds in the __new__ method, and I'm calling type.__new__ directly. This means that if you try to use RandomMeta together with another metaclass (besides type), you should get an immediate TypeError, rather than something that may or may not be correct.
Meanwhile, I have a suspicion that what you're really trying to do here is build a prototype-based inheritance system, a la Self or JavaScript on top of Python's class-based system. While you can do that by using a special Prototype metaclass and a bunch of class objects, it's a whole lot simpler to just have a Prototype class and a bunch of instance objects. The only advantage to the metaclass approach is that you can use class statements (misleadingly, but conveniently) to clone prototypes, and you're explicitly not doing that here.
While my other answer covers the question as asked, I suspect it's all completely unnecessary to the OP's actual problem.
If you just want to create a bunch of separate objects, which each have a separate value for exampleData, you just want a bunch of instances of a single class, not a bunch of separate classes.
A class is a special kind of object that, in addition to doing all the normal object stuff, also works as a factory for other objects, which are instances of that class. You don't need a, b, and c to all be factories for for different kinds of objects, you just need them to be different objects of the same type. So:
class RandomThing:
def __init__(self):
self.exampleData = random.randint(1,100)
a = RandomThing()
b = RandomThing()
… or, if you want to make sure b is the same type of thing as a but don't know what type that is:
b = type(a)()
That's as fancy as you need to get here.
See the official tutorial on Classes (or maybe search for a friendlier tutorial, because there are probably better ones out there).

Why does x = Class() create a new object in python?

This is my first programming language, so be gentle. I was doing swimmingly in reading my book before OOP came up and I've been terribly lost. I bought a new book just on OOP in Python and I still can't grasp the basics.
First, I was struggling with the "self" concept, but I'm conceptually lost on an even more fundamental level.
Why does x = Class() create a new instance of that class? Wouldn't it just refer to class?
If I write y = Class(), too, how come you don't wind up with two different variables that refer to the same thing even though I defined them as the same thing? Why not have language like "Instantiate("name_of_new_instance") Class()"?
I don't understand what's going on here.
Edit: A lot of answers so quickly! So am I to understand that the equals sign here is arbitrary, like the programming equivalent of a homophone? (homograph?) Why was it chosen that way, it doesn't seem very intuitive. I'm not criticizing it, is there a historical reason? Is there some logic going on underneath that is lost on beginners?
The reference to the class itself is just Class. Writing Class() calls the class, which returns an instance of the class.
def foo():
return 42
print foo
print foo()
class Class(object):
pass
print Class
print Class()
You can see the instatiation of one object member of a class like so:
class Foo(object):
def __new__(cls):
print 'new Foo'
return super(Foo, cls).__new__(cls)
def __init__(self):
print 'init Foo'
>>> foo=Foo()
new Foo
init Foo
In Python, the () indicates a call of the class (or function or method). For a class, that first calls new then __init__
When you do x = Class(), you are effectively creating a new object instance of the class Class called x. This is what OOP is all about.
The self variable is used in classes to refer to variables that are specific to that particular instance of the class.
For example:
class Dog:
def __init__(self,name,type):
self.name = name
self.type = type
self.mood = "happy"
def change_mood(self):
if self.mood == "happy":
self.mood = "sad"
else:
self.mood = "happy"
dog1 = Dog("Adam","big")
dog2 = Dog("James","small")
dog1.change_mood()
>>> dog1.mood
"sad"
>>> dog2.mood
"happy"
You've covered functions, right?
Classes act like functions that produce new objects. Class() means you're calling it, and every time you call it, you get a new object. That's just what classes do when called.
x = Class is very different from x = Class(). The former will, indeed, just make an alias for the class.
As for "why", well, it's actually pretty handy at times to be able to substitute a function for a class or vice versa. For example, the int() function isn't a function at all; you're just creating a new int object.
As for =, well, there's no excuse for that :) Most languages use a = b to mean "take b and store it in a", not to mean a and b are equal. Historical reasons, I suppose.
You ask:
Why not have language like "Instantiate("name_of_new_instance")
Class()"?
The answer is that Python is exactly like that language - except that it leaves out the unnecessary word Instantiate and instead uses an equal sign to indicate that assignment is taking place. Other than that small (and meaningless) difference in syntax the languages are the same.
While I like the use of the keyword Instantiate in your language because it's very clear about what's happening, I also think Python's design has a number of advantages:
It's less verbose.
It is clearer that an assignment is taking place.
It provides a more obvious place to place any arguments required when initializing a new instance of Class
It will be more familiar to most programmers coming from c-descended languages.
Once you have experience with a number of different languages, I hope you'll share my appreciation for the clever decisions that the designer of Python made and that make (good) Python code both clear and extremely concise. Of course, you may feel otherwise in which case you'll find a world of syntaxes available in many different languages or, perhaps, you'll find a need to develop your own.

Explaining Python variable scope

I'm teaching myself Python and I was translating some sample code into this
class Student(object):
def __init__( self, name, a,b,c ):
self.name = name
self.a = a
self.b = b
self.c = c
def average(self):
return ( a+b+c ) / 3.0
Which is pretty much my intended class definition.
Later in the main method I create an instance and call it a:
if __name__ == "__main__" :
a = Student( "Oscar", 10, 10, 10 )
That's how I find out that the variable a declared in main is available to the method average and to make that method work, I have to type self.a + self.b + self.c instead.
What's the rationale for this?
Barenames (like a, b, c) are always scoped as local or global (save for nested functions, which are nowhere around in your code). The rationale is that adding further scopes would needlessly make things more complicated -- e.g, if in your self.a = a the barename a could be scoped to mean what you appear to want (equivalent to self.a) then the assignment itself would be meaningless (assigning a name to itself), so you'd need further complicated rules.
Just using qualified names (like self.a) when you want something different than barenames' simple, straightforward, and optimized behavior, is by far the simplest approach -- perfectly workable, no complicated rules whatsoever, and allows the compiler to optimize things effectively (since e.g. a barename's scope is always lexically determined, not dependent on dynamically varying characteristics of the environment). So, besides perhaps nostalgia for other language with more complicated scoping rules, there's really no rationale for complicating the semantics of barenames.
There are several reasons, though the main one is from the Zen of Python: "Explicit is better than implicit." In a language like C++, a method on the class always has an implicit argument this which is pushed onto the stack every time the method is called. In this case, when an instance variable b exists as well as a global variable b, then the user may just refer to b referring to one without realizing that the other will be used. So Python forces you to be explicit about your scope to avoid confusion.
With that being said, there are other reasons as well. For example, I may define a function outside of a class and then attach it to a class at runtime. For example:
def log(self):
print "some library function requires all objects to have a log method"
print "unfortunately we're using the Student class, which doesn't have one"
print "this class is defined in a separate library, so we can't add the method"
print "fortunately, we can just add the method dynamically at runtime"
Student.log = log
Here the fact that self is explicit makes it trivial for us to define a function outside of a class and then attach it to that class. I don't do this sort of thing incredibly often, but it's EXTREMELY useful when I do.
Here's an even more complex example; suppose we want to define a class inside another class, such as for the purposes of unit testing:
class SomeUnitTests(TestCase):
def test_something(self):
class SomeMockObject(SomeActualObject):
def foo(self2):
self.assertEqual(self2.x, SOME_CONSTANT)
some_lib.do_something_with(SomeMockObject)
Here the presence of an explicit self (which we can call whatever we want, it doesn't have to be self) allows to to distinguish between the self of the inner and outer classes. Again, this isn't something I do frequently, but when I do then it's incredibly useful.
All instance variables should be called using self

Why do new instances of a class share members with other instances?

class Ball:
a = []
def __init__(self):
pass
def add(self,thing):
self.a.append(thing)
def size(self):
print len(self.a)
for i in range(3):
foo = Ball()
foo.add(1)
foo.add(2)
foo.size()
I would expect a return of :
2
2
2
But I get :
2
4
6
Why is this? I've found that by doing a=[] in the init, I can route around this behavior, but I'm less than clear why.
doh
I just figured out why.
In the above case, the a is a class attribute, not a data attribute - those are shared by all Balls(). Commenting out the a=[] and placing it into the init block means that it's a data attribute instead. (And, I couldn't access it then with foo.a, which I shouldn't do anyhow.) It seems like the class attributes act like static attributes of the class, they're shared by all instances.
Whoa.
One question though : CodeCompletion sucks like this. In the foo class, I can't do self.(variable), because it's not being defined automatically - it's being defined by a function. Can I define a class variable and replace it with a data variable?
What you probably want to do is:
class Ball:
def __init__(self):
self.a = []
If you use just a = [], it creates a local variable in the __init__ function, which disappears when the function returns. Assigning to self.a makes it an instance variable which is what you're after.
For a semi-related gotcha, see how you can change the value of default parameters for future callers.
"Can I define a class variable and replace it with a data variable?"
No. They're separate things. A class variable exists precisely once -- in the class.
You could -- to finesse code completion -- start with some class variables and then delete those lines of code after you've written your class. But every time you forget to do that nothing good will happen.
Better is to try a different IDE. Komodo Edit's code completions seem to be sensible.
If you have so many variables with such long names that code completion is actually helpful, perhaps you should make your classes smaller or use shorter names. Seriously.
I find that when you get to a place where code completion is more helpful than annoying, you've exceeded the "keep it all in my brain" complexity threshold. If the class won't fit in my brain, it's too complex.

Categories

Resources