What is the convention for referencing class attributes? I understand that there are two ways of referencing class attributes:
class Circle1:
pi = 3.14
def __init__(self, radius=1):
self.radius = radius
def get_circumference():
return 2 * self.pi * self.radius
class Circle2:
pi = 3.14
def __init__(self, radius=2):
self.radius = radius
def get_circumference():
return 2 * Circle2.pi * self.radius
The first way is referencing it as self.attribute, while the second way is to reference it as Class.attribute. Which way is the convention for referencing class attributes? Or is there no convention and is it just a matter of preference?
It really depends on how you're going to use it. Class.attribute will set it as an attribut for all instances of that class, while self.attribute is only for specific instances. In your case, pi is always (around) 3.14, so it should be a class attribute, while the radius is specific to any one circle, so it should be set as self.radius.
Also, to help shorten your code, you should only use 1 class of circles, and make the radius mandatory (don't specify default) because that is the only thing that changes.
Related
I have an external class to represent my data idk. Inside the class idk I want to have another class change which will contain various methods to change instances of the class idk. What I've tried is below. Note: this is a simplified example of my actual code
class idk:
def __init__(self):
self.x=1
self.y=2
class change:
def func(self):
self.x=10
self.y=100
var=idk()
var.change.func()
print(var.x, var.y)
However this gives the error:
TypeError: func() missing 1 required positional argument 'self'
How do I change the code to make it work?
Well, first of all, your are getting this error because you are accessing the func function as a class attribute and not by an instance of it (putting a class definition inside another class won't make it an instance).
If it makes sense, you cloud put those "change methods" in the idk class directly (that would be a normal approach):
class idk:
def __init__(self):
self.x = 1
self.y = 2
def func(self):
self.x = 10
self.y = 100
var = idk()
var.func()
print(var.x, var.y) # Output: 10 100
If you really want/need to separate those methods, you could do another class. The way I would implement that class is with static methods where all of them recieve an idk instance as the first parameter:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class idkChanger:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idkChanger.func(var)
print(var.x, var.y) # Output: 10 100
If you really really want/need to have that "changer" class inside of the idk class you can define it there, but this is not common at all. Also, you will have to pass the instance as well, that Changer class:
class idk:
def __init__(self):
self.x = 1
self.y = 2
class Changer:
#staticmethod
def func(idk_obj):
idk_obj.x = 10
idk_obj.y = 100
var = idk()
idk.Changer.func(var)
print(var.x, var.y) # Output: 10 100
Final notes:
You could not mark (decorate) the func as static and it will work the same, but this will bring more confution for several reasons (e.g., you would tecnically saying that func is an instance method. Which is not, because the objects you want to change are not Change's instances but idk's).
I have a class, for example Circle, which has dependent attributes, radius and circumference. It makes sense to use a dataclass here because of the boilerplate for __init__, __eq__, __repr__ and the ordering methods (__lt__, ...).
I choose one of the attributes to be dependent on the other, e.g. the circumference is computed from the radius. Since the class should support initialization with either of the attributes (+ have them included in __repr__ as well as dataclasses.asdict) I annotate both:
from dataclasses import dataclass
import math
#dataclass
class Circle:
radius: float = None
circumference: float = None
#property
def circumference(self):
return 2 * math.pi * self.radius
#circumference.setter
def circumference(self, val):
if val is not type(self).circumference: # <-- awkward check
self.radius = val / (2 * math.pi)
This requires me to add the somewhat awkward check for if val is not type(self).circumference because this is what the setter will receive if no value is provided to __init__.
Then if I wanted to make the class hashable by declaring frozen=True I need to change self.radius = ... to object.__setattr__(self, 'radius', ...) because otherwise this would attempt to assign to a field of a frozen instance.
So my question is if this is a sane way of using dataclasses together with properties or if potential (non-obvious) obstacles lie ahead and I should refrain from using dataclasses in such cases? Or maybe there is even a better way of achieving this goal?
For starters, you could set the attributes in the __init__ method as follows:
from dataclasses import dataclass, InitVar
import math
#dataclass(frozen=True, order=True)
class CircleWithFrozenDataclass:
radius: float = 0
circumference: float = 0
def __init__(self, radius=0, circumference=0):
super().__init__()
if circumference:
object.__setattr__(self, 'circumference', circumference)
object.__setattr__(self, 'radius', circumference / (2 * math.pi))
if radius:
object.__setattr__(self, 'radius', radius)
object.__setattr__(self, 'circumference', 2 * math.pi * radius)
This will still provide you with all the helpful __eq__, __repr__, __hash__, and ordering method injections. While object.__setattr__ looks ugly, note that the CPython implementation itself uses object.__setattr__ to set attributes when injecting the generated __init__ method for a frozen dataclass.
If you really want to get rid of object.__setattr__, you can set frozen=False (the default) and override the __setattr__ method yourself. This is copying how the frozen feature of dataclasses is implemented in CPython. Note that you will also have to turn on unsafe_hash=True as __hash__ is no longer injected since frozen=False.
#dataclass(unsafe_hash=True, order=True)
class CircleUsingDataclass:
radius: float = 0
circumference: float = 0
_initialized: InitVar[bool] = False
def __init__(self, radius=0, circumference=0):
super().__init__()
if circumference:
self.circumference = circumference
self.radius = circumference / (2 * math.pi)
if radius:
self.radius = radius
self.circumference = 2 * math.pi * radius
self._initialized = True
def __setattr__(self, name, value):
if self._initialized and \
(type(self) is __class__ or name in ['radius', 'circumference']):
raise AttributeError(f"cannot assign to field {name!r}")
super().__setattr__(name, value)
def __delattr__(self, name, value):
if self._initialized and \
(type(self) is __class__ or name in ['radius', 'circumference']):
raise AttributeError(f"cannot delete field {name!r}")
super().__delattr__(name, value)
In my opinion, freezing should only happen after the __init__ by default, but for now I will probably use the first approach.
I'm sure this will be a duplicate question, but I can't seem to find the words to locate one.
I have a set of very similar models I'd like to code up. The models are all the same, apart from a single function / line of code. I'd like to avoid any code repetition. Let' see an MWE:
import numpy as np
class SinModel:
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
# Computations which are invariant of the function we use later
self.y = np.sin(self.x)
# More computations which are invariant of which funcion was used
Our second model will involve the same series of computations, but will use a different function mid way though (here, cosine instead of sine):
class CosModel:
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
# Computations which are the same as in SinModel
self.y = np.cos(self.x)
# More computations which are the same as in SinModel
Here I have lots of code repetition. Is there a better way to implement these models? I was hoping it would be possible to create a class Model which could inherit the differing function from an arbitrary class.
An important note is that the function which changes between models may take different arguments from self depending on the model.
The words you're looking for are inheritance (allowing a class to inherit and extends / specialize a parent class) and the "template method" design pattern (which is possibly the most common design pattern - the one everyone discovers by itself long before reading about design patterns).
Expanding on your MWE:
import numpy as np
class ModelBase(object):
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
# Computations which are invariant of the function we use later
self.y = self.compute_y()
# More computations which are invariant of which funcion was used
def compute_y(self):
raise NotImplementedError("class {} must implement compute_y()".format(type(self).__name__))
class SinModel(ModelBase):
def compute_y(self):
return np.sin(self.x)
class CosModel(ModelBase):
def compute_y(self):
return np.cos(self.x)
This being said, creating instance attributes outside the initializer (the __init__ method) is considered bad practice - an object should be fully initialized (have all it's attributes defined) when the initializer returns, so it might be better to move the self.y = self.compute_y() line to the initializer if possible, or, if self.y always only depends on self.x, make it a computed attribute:
class ModelBase(object):
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
#property
def y(self):
return self._compute_y()
def _compute_y(self):
raise NotImplementedError("class {} must implement _compute_y()".format(type(self).__name__))
def run(self):
# Computations which are invariant of the function we use later
# no need to explicitely set self.y here, just use `self.y`
# and it will delegate to self._compute_y()
#(you can't set it anymore anyway since we made it a readonly propery)
# More computations which are invariant of which funcion was used
class SinModel(ModelBase):
def _compute_y(self):
return np.sin(self.x)
class CosModel(ModelBase):
def _compute_y(self):
return np.cos(self.x)
Also at this point you don't necessarily need subclasses anymore, at least if that's the only thing that changes - you can just pass the proper function as a callback to your model class ie:
class Model(object):
def __init__(self, compute_y):
self.x = np.linspace(-np.pi, np.pi)
self._compute_y = compute_y
#property
def y(self):
return self._compute_y(self)
def run(self):
# code here
cos_model = Model(lambda obj: np.cos(obj.x))
cos_model.run()
sin_model = Model(lambda obj: np.sin(obj.x))
sin_model.run()
Yes, and there's even a name for it: Inheritance is the idea that child classes can "inherit" behaviors and attributes from parent classes, and Polymorphism is the idea that two child classes, sharing similar behavior, can have different implementations of the same method - so that you can call a method on an object without knowing explicitly what type it is, and still have it do the right thing.
Here's how you'd do that in python:
class TrigModel:
def __init__(self):
self.x = np.linspace(-np.pi, np.pi)
def run(self):
raise NotImplementedError("Use subclasses SinModel or CosModel")
class SinModel(TrigModel):
#override
def run(self):
self.y = np.sin(self.x)
class CosModel(TrigModel):
#override
def run(self):
self.y = np.cos(self.x)
Unless you explicitly specify otherwise (by declaring a method like run() that overrides the parent class's method of the same name), SinModel and CosModel will call TrigModel's methods on themselves (in this case, they both call TrigModel's constructor, but then display different behavior when you call run() on them).
If you then do:
model.run()
then model will behave differently depending on whether it's a SinModel or a CosModel, depending on what you set it to beforehand.
The #override decorator isn't strictly necessary, but it's good practice to lessen ambiguity.
Trying to get my head around property decorators. I found a solution posted for setting read-only attributes here. Setting a private attribute and then providing a #property getter method makes sense if you can specify the attribute in init. But what about the case where you want to use a function to calculate a read-only attribute? Let's say you have a class that calls an attribute (e.g. state) from another class and then calculates a new value that will be made available as an attribute:
class MyState(object):
def __init__(self, starting_value):
self._foo = starting_value
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, value):
self._foo = value
class MyClass(object):
def __init__(self, name=None):
self.name = name
#property
def bar(self):
state = MyState.foo
return id(state)
>mystate = MyState('chuff')
>myclass = MyClass()
>myclass.bar = 183097448L
In everything I have seen about property decorators, I have only see display methods reflected in the #property getter function, never functions that set the value of the variable. However, from reading the docs my understanding is that #setter requires an argument, which I don't have in this case. Is there any problem with calculating the read-only value of a class attribute in the #property getter method as opposed to simply passing an attribute that already exists?
There is no problem. #property is just doing less than you think. All it is is a bit of syntactic sugar to replace: a = foo.x with a = foo.x.getter(), and foo.x = bar with foo.x.setter(bar). That is, it allows you to replace attribute access with method calls. Those methods are allowed to do anything they like, which is the purpose of the property. I think you were being led astray by your first example where the property just passes through to an underlying hidden variable to make a psuedo-read-only variable. That is not really the standard use case. A very common example might be:
class Rectangle(object):
def __init__(self, w, h):
self.w = w
self.h = h
#property
def area(self):
return self.w * self.h
Area is a property of a rectangle, but it is derived from the width and height, and setting it doesn't really make any sense.
I'm learning Python OOP now and confused with somethings in the code below.
Questions:
def __init__(self, radius=1):
What does the argument/attribute "radius = 1" mean exactly?
Why isn't it just called "radius"?
The method area() has no argument/attribute "radius".
Where does it get its "radius" from in the code?
How does it know that the radius is 5?
class Circle:
pi = 3.141592
def __init__(self, radius=1):
self.radius = radius
def area(self):
return self.radius * self.radius * Circle.pi
def setRadius(self, radius):
self.radius = radius
def getRadius(self):
return self.radius
c = Circle()
c.setRadius(5)
Also,
In the code below, why is the attribute/argument name missing in the brackets?
Why was is not written like this: def __init__(self, name)
and def getName(self, name)?
class Methods:
def __init__(self):
self.name = 'Methods'
def getName(self):
return self.name
The def method(self, argument=value): syntax defines a keyword argument, with a default value. Using that argument is now optional, if you do not specify it, the default value is used instead. In your example, that means radius is set to 1.
Instances are referred to, within a method, with the self parameter. The name and radius values are stored on self as attributes (self.name = 'Methods' and self.radius = radius) and can later be retrieved by referring to that named attribute (return self.name, return self.radius * self.radius * Circle.pi).
I can heartily recommend you follow the Python tutorial, it'll explain all this and more.
def __init__(self, radius=1):
self.radius = radius
This is default value setting to initialize a variable for the class scope.This is to avoid any garbage output in case some user calls c.Area() right after c = Circle().
In the code below, why is the attribute/argument "name" missing in the brackets?
In the line self.name = 'Methods' you are creating a variable name initialized to string value Methods.
Why was is not written like this: def init(self, name) and def
getName(self, name)?
self.name is defined for the class scope. You can get and set its value anywhere inside the class.
The syntax radius = 1 specifies a parameter "radius" which has a default value of 1:
def my_func(param=1):
... print(param)
...
my_func() #uses the default value
1
my_func(2) #uses the value passed
2
Note that in python there exists more kinds of parameters: positional and keyword parameters, or both.
Usually parameters can be assigned both using the positional notation and the keyword:
>>> def my_func(a,b,c):
... print (a,b,c)
...
>>> my_func(1,2,3)
(1, 2, 3)
>>> my_func(1,2,c=3)
(1, 2, 3)
Python uses "explicit" instance passing, so the first self parameter is used to pass the instance on which the methods are called. You can think of self as being the this of Java. But you must always use it to access instance attributes/methods. You can't call just area(), you must say self.area().
When you do self.attribute = 1 you create a new attribute attribute with value 1 and assign it to the instance self. So in the area() method self.radius refers to the radius attribute of the self instance.
The __init__ method is a special method. It's something similar to a constructor.
It is called when you instantiate the class. Python has a lot of these "special methods", for example the method __add__(self, other) is called when using the operator +.