Is it possible to get the name of a field reflectively in Python (3.2)?
See the following example:
class Something:
def __init__(self):
self.x = 1
def validate():
return validator.max(self.x, 10)
validator.max(self.x, 10) should produce an error message containing the name of the field x as string (in this case "x").
You would have to pass the attribute name as a string
def validate():
return validator.max(self, "x", 10)
then validator.max might look like this
def max(ob, attr, max_value):
val = getattr(ob, attr) # val would be self.x now
...
Unlikely. Things might not even have names - what is:
validator.max(3,10)
supposed to do?
Pass the name as well as the value if you want it output:
validator.max(self.x,10,"x")
whatever validator.max is, it needs another argument, or if its a builtin, you need to wrap it.
In Python the expression self.x is just the current value of that member and the information that the value is coming from an object is therefore lost.
However you can move the validation logic at an higher level (base class) and having it working on a whole object. With this approach validator "name" will be known by the validate function and can be used for the error message:
class ValidatedObject:
def validate(self):
for name in dir(self):
if (name.startswith("validate_") and # Is a validator
not getattr(self, name)()): # and failed
raise RuntimeError("%s: %s" %
(name, getattr(self, name).__doc__))
class Something(ValidatedObject):
def __init__(self, x, y):
self.x = x
self.y = y
def validate_x(self):
"Horizontal position shouldn't be that big"
return self.x < 10
def validate_y(self):
"Vertical position must be neither too low nor too high"
return 20 <= self.y <= 30
def validate_sum(self):
"The position must be on the prescribed line"
return self.x + self.y == 25
class Something2(Something):
def validate_sum(self):
return True
Something(3, 22).validate() # Ok
Something2(5, 30).validate() # Ok (derived class relaxed checks)
print "About to crash...."
Something2(5, 31).validate() # Not ok (Y is too high - inherited check)
Note of course that disabling a check in a derived class is illogical from an IS-A point of view, here is just as an example showing that dir will correctly find inherited members.
Related
I'm creating a chess game in which I decided to implement two class, the piece class an abstract class extends to various abstract operations(Pawn, King, Queen, Rook, Knight, Bishop), and every piece will be placed on a spot. The spot class represents one block of the 8x8 grid and an optional piece. In my spot class I take in an instance of the piece class in the constructor but I am getting an error "No statement effect errors". I am not sure why?
class Spot:
def __init__(self, x, y,Piece piece):
self.x = x
self.y = y
self.piece = piece
class Piece:
killed = False
white = False
def __init__(self, white,killed):
self.white = white
self.killed = killed
def iswhite(self):
return self.white == True
def iskilled(self):
return self.killed == True
In Python, you don't need to declare the type of your arguments, like in Java, C#, C++, etc. Python is dynamically typed, so the interpreter will figure out what objects you're passing during run time.
Change your code as follows:
class Spot:
def __init__(self, x, y, piece):
self.x = x
self.y = y
self.piece = piece
If you really want to specify the data types, you can use a feature of Python called type hinting as follows:
class Spot:
def __init__(self, x: int, y: int, piece: Piece):
self.x = x
self.y = y
self.piece = piece
A few other pointers:
You don't need to compare boolean operators using x == True, you can simply return the boolean variable x, which will have the same effect.
You should always use snake_case in Python, so is_white instead of iswhite.
You don't need to initialize the variables in the Piece class like that. You can do that in the __init__ method directly using default arguments. So if a user does not provide the argument, the default value will be used for that argument.
class Piece:
def __init__(self, white=False, killed=False):
self.white = white
self.killed = killed
def is_white(self):
return self.white
def is_killed(self):
return self.killed
I want to have a Class which can be initialized with options a,b and c.
c is a special case, where I can modify the initialization with a variable extend.
I'm currently looking for the best way to do this.
Also I would like my IDE (in this case PyCharm) to make suggestions to me which parameters I can use for the preset.
I came up with two ideas to do it.
Option 1:
class MyClass:
def __init__(self,preset,extend=None):
if preset == "a":
self.x = 1
if preset == "b":
self.x = 2
if preset == "c":
self.x = 3
if extend != None:
self.x = self.x + extend
def __str__(self):
return f"The value of x is {self.x}"
Y=MyClass(preset="c",extend= 3)
print(Y)
#out: The value of x is 6
Option 2:
class MyClass2:
def __init__(self):
self.x=None
def preset_a(self):
self.x=1
def preset_b(self):
self.x=2
def preset_c_with_extend(self,extend):
self.x =3+extend
def __str__(self):
return f"The value of x is {self.x}"
Y2=MyClass2()
Y2.preset_b()
print(Y2)
#out: The value of x is 2
Option 1 looks more elegant to me, but in my workflow I don't want to go to the implementation for initializing a certain preset for looking up the options.
But this would be necessary, because I can not remember for bigger projects if I named the preset a or if it was not A.
Option 1 also leaves it unclear if I can add an option extend.
Here it might happen, that I use preset a with extend=3 and I am wondering why the extend is not applied.
So the actual question: Is there an elegant way to see the preset options without looking at the class implementation? (Maybe some kind of Type Hint?)
Option 2 has this opportunity, and with auto-completion in my IDE I see what presets I can apply. But it doesn't look very elegant.
I am curious about your ideas!
How about:
class MyClass2:
def __init__(self, x):
self.x = x
#staticmethod
def preset_a():
return MyClass2(1)
#staticmethod
def preset_b():
return MyClass2(2)
#staticmethod
def preset_c_with_extend(extend):
return MyClass2(3+extend)
def __str__(self):
return f"The value of x is {self.x}"
Y2=MyClass2.preset_b()
print(Y2)
It ensures that x is set at object creation time and should allow IDE auto-completion.
Another alternative is to use a presets dict. However, I have no idea how PyCharm will treat this solution in regards to suggestions.
class MyClass:
PRESETS = {"a": 1, "b": 2, "c": 3}
def __init__(self, preset, extend=None):
self.x = self.PRESETS.get(preset)
if preset == "c" and extend is not None:
self.x += extend
def __str__(self):
return f"The value of x is {self.x}"
Note that dict's .get() method is used which means x will be None if you try to use a non-existing preset.
Given the code:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
class Block:
size = 40
def __init__(self, x=1, y=1):
self.pixel_position = Point(x * Block.size, y * Block.size)
self.__block_position = Point(x, y)
#property
def block_position(self):
return self.__block_position
#block_position.setter
def block_position(self, point):
#I want for pixel_position to be updated whenever block position changes
self.pixel_position.x += point.x * size
self.pixel_position.y += point.y * size
self.__block_position = point
Now for such code simple assignment works well
block = Block()
block.block_position = Point(2, 1)
but if I want to increment x of block position... well, code doesn't go into setter.
block.block_position.x -= 1
# now block_position = (1, 1) but pixel_position = (80, 40) not (40, 40)
How may I change it?
I know I can resolve this problem with adding property for __pixel_position that will calculate Block.size * __block_position before returning itself, but that approach doesn't satisfy me - well I want to know how in python one can set a property for a field of a field.
My question is not about finding any solution, but to find a solution where changing field block_position.x will redirect me to my setter/getter.
The problem there is that you are mixing some concepts trying to make use of Python property and things get confusing -
The basic thing is that if pixel_position is to be to be calculated - it should itself a property.
What your are trying to do is to put some gates on the setting of values on "block_position" and derive - when block_position is changed the new value for pixel_position. That is not working because you have not "gatted" always one could possibly modify the values inside your block_position.
What happens when you make:
block.block_position.x += 1
Is that the property getter for block_position is activated - the Point object in there then have its x attribute changed - but this change never goes through the outter block object,as x is an ordinay attribute, not a property.
Now, it would be possible to instrument your Point class so that actions could be triggered whenever x ou y are changed - but that could become really complicated, and fast.
A better approache there is to have pixel_position itself be a property, instead of an ordinary attribute, and have its values lazily calculated - generated whenever they are needed - and not depend on the value to be setted eagerly whenever block_position changes. This is a pattern from "reactive programing".
Actually, you will find out that "block_poisition" itself can be an ordinary instance attribute, and not be a property - unless you want its checker to ensure the assigned object is an instance of Point.
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __mul__(self, scale):
return Point(self.x * scale, self.y * scale)
class Block:
size = 40
def __init__(self, x=1,y=1):
self._block_position = Point(x,y)
#property
def block_position(self):
return self._block_position
#block_position.setter
def block_position(self, point):
if not isinstance(point, Point):
# raise TypeError() # or, as an option, accept sequences of 2 coordinates:
point = Point(point[0], point[1])
self._block_position = point
#property
#pixel_position(self):
return self._block_position * self.size
So, now things are the other way around - and pixel_position can't be setted and is guaranteed to be always updated.
Three things in there:
I've added the __mul__ method to your Point so now one can just use the * operator to multiply it by a scalar.
There is no need or sense in actually prepending __ to Python "hidden" attributes. Early tutorials or unofficial documentation to the language could misleadingly say it is a way of having "private attributes". That is an error - there is no private attributes in Python what __ does is name mangling in order to allow a class to have attributes that are not messed up by subclasses. In almost 20 years of Python programming I've never actually needed that feature. On the other hand, it can give you strange errors if you have subclasses of Block. Just don't. The accepted convention is that one single "_" indicates an attribute that should not be changed or accessed directly by users of the class and this have no side effects.
Without a setter, pixel_position is mostly "unchangeable" - and if one does change the attributes inside it after retrieving block.pixel_position , he will be changing a detached Point instance.
If you really need round-trip changing between pixel_position and block_position (that is, make in such a way the class user can change either attribute and have the change reflected in the other), rather than trying to instrument a change notification inside Point , I suggest you make Point an immutable class instead. Anyone wanting to change coordinates would have to create a new point instance - as a result block.block_position.x += 1 would not work anymore - one would have to do: block.block_position += Point(1, 0) (and then you implement __add__ in Point, just as I did __mul__). Then you could write your setter for pixel_position to force a new value to block_position if it gets changed.
On the upside, if you make Point immutable, you can add __hash__ to it and have it working in sets and as dictionary keys: a lot of other uses open up.
Check the class V2 implementation on this project to have an idea of a nice implementation (Disclaimer: the link project is something I am working now as a hobby, and I have actually implemented this class over the past week)
Since you asked for it, I'm providing an example implementation where properties of an object notify their owner when being set, for it to synchronize with another object. I only consider 1D points (x) in this example since the implementation for y is the very same:
class NotificationProperty(property):
def __init__(self, fget=None, fset=None, fdel=None, doc=None, notify=None):
super().__init__(fget, fset, fdel, doc)
self.notify = notify
def __set_name__(self, owner, name):
self.name = name
def __set__(self, instance, val):
super().__set__(instance, val)
self.notify(instance, self.name, val)
# Should define similar methods for other attributes
# (see https://docs.python.org/3/howto/descriptor.html#properties).
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__, self.notify)
def notification_property(func):
from functools import partial
return partial(NotificationProperty, notify=func)
class SyncPoint:
def __init__(self, x, sync_with=None):
self.sync_with = sync_with
self.x = x
def sync(self, which, value):
if self.sync_with is not None:
obj, scale = self.sync_with
value = int(scale * value)
if getattr(obj, which) != value: # Check if already synced -> avoid RecursionError.
setattr(obj, which, value)
#notification_property(sync)
def x(self):
return self._x
#x.setter
def x(self, val):
self._x = val
class Block:
size = 40
def __init__(self, x=1):
self.pixel_position = SyncPoint(self.size * x)
self.block_position = SyncPoint(x, sync_with=(self.pixel_position, self.size))
self.pixel_position.sync_with = (self.block_position, 1/self.size)
block = Block(3)
print('block_pos: ', block.block_position.x) # block_pos: 3
print('pixel_pos: ', block.pixel_position.x) # pixel_pos: 120
block.block_position.x -= 1
print('block_pos: ', block.block_position.x) # block_pos: 2
print('pixel_pos: ', block.pixel_position.x) # pixel_pos: 80
block.pixel_position.x -= Block.size
print('block_pos: ', block.block_position.x) # block_pos: 1
print('pixel_pos: ', block.pixel_position.x) # pixel_pos: 40
Variation: specify the notify function via x.setter(func)
The following is a variation of the above code which let's you specify the function to be called for notifications during definition of x.setter. This might feel more intuitive since the notification happens on __set__ but in the end it's a matter of taste:
from functools import partial
class notification_property(property):
def __init__(self, fget=None, fset=None, fdel=None, doc=None, notify=None):
super().__init__(fget, fset, fdel, doc)
self.notify = notify
def __set_name__(self, owner, name):
self.name = name
def __set__(self, instance, val):
super().__set__(instance, val)
self.notify(instance, self.name, val)
# Should define similar methods for other attributes
# (see https://docs.python.org/3/howto/descriptor.html#properties).
def setter(self, func=None):
return partial(type(self), self.fget, fdel=self.fdel, doc=self.__doc__, notify=(func or self.notify))
class SyncPoint:
def __init__(self, x, sync_with=None):
self.sync_with = sync_with
self.x = x
def sync(self, which, value):
if self.sync_with is not None:
obj, scale = self.sync_with
value = int(scale * value)
if getattr(obj, which) != value: # Check if already synced -> avoid RecursionError.
setattr(obj, which, value)
#notification_property
def x(self):
return self._x
#x.setter(sync)
def x(self, val):
self._x = val
You can resolve this by making pixel_position a property since this attribute seems dependent on the other, block_position. That way block_position doesn't even have to be a property:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f'{type(self).__name__}({self.x}, {self.y})'
class Block:
size = 40
def __init__(self, x=1, y=1):
self.block_position = Point(x,y)
#property
def pixel_position(self):
return Point(self.block_position.x * self.size, self.block_position.y * self.size)
block = Block()
block.block_position = Point(2,1)
block.block_position.x -= 1
print(block.block_position) # Point(1, 1)
print(block.pixel_position) # Point(40, 40)
In general dependent attributes are suitable for properties as they can be computed "on-the-fly" from other, independent ones.
I am reading Item 31 in the book "Effective Python". I don't understand why in the example on page 97, why math_grade, writing_grade and science_grade are class (static) variables of the Exam class, rather than regular, instance variables. If they were instance variables, then the Grade class wouldn't need to use instance as key in its global book-keeping dictionary. Seems to me like the author made one obvious design mistake just to illustrate how to use descriptors, i.e. global book keeping in the Grade class, which seems like a bad idea anyways.
My other question is more high-level: isn't this a confusing, unclear way to do things? Keeping global state of multiple objects in one single registry, like Grade does. Doesn't seem like a reusable, clean design to me.
Here is reference to the code, for people that don't have the book:
https://github.com/SigmaQuan/Better-Python-59-Ways/blob/master/item_31_use_descriptors.py
Specifically
class Grade(object):
def __get__(*args, **kwargs):
super().__getattribute__(*args, **kwargs)
def __set__(*args, **kwargs):
super().__setattr__(args, kwargs)
class Exam(object):
math_grade = Grade()
writing_grade = Grade()
science_grade = Grade()
I think a good reference in the subject available to everyone, is actually the official docs in this Descriptors How To
I setup an example, but note that there is a lot more about descriptors, and you shouldn't be using this unless writing a framework or some library (like an ORM) that requires the dynamic instantiation and validation of fields of different types for example.
For the usual validation needs, limit yourself to property decorator.
class PositionX: # from 0 to 1000
def __init__(self, x):
self.x = x
print('***Start***')
print()
print('Original PositionX class')
pos1 = PositionX(50)
print(pos1.x)
pos1.x = 100
print(pos1.x)
pos1.x = -10
print(pos1.x)
print()
# let's validate x with a property descriptor, using #property
class PositionX: # from 0 to 1000
def __init__(self, position):
self._x = position
#property
def x(self):
return self._x
#x.setter
def x(self, value):
if 0 <= value <= 1000:
self._x = value
else:
raise ValueError
print('PositionX attribute x validated with #property')
pos2 = PositionX(50)
print(pos2.x)
pos2.x = 100
print(pos2.x)
try:
pos2.x = -10
except ValueError:
print("Can't set x to -10")
print()
# Let's try instead to use __set__ and __get__ in the original class
# This is wrong and won't work. This makes the class PositionX a descriptor,
# while we wanted to protect x attribute of PositionX with the descriptor.
class PositionX: # from 0 to 1000
def __init__(self, x):
self.x = x
def __get__(self, instance):
print('Running __get__')
return self._x
def __set__(self, instance, value):
print('Running __set__')
if 0 <= value <= 1000:
self._x = value
else:
raise ValueError
print("Using __set__ and __get__ in the original class. Doesn't work.")
print("__get__ and __set__ don't even run because x is found in the pos3 instance and there is no descriptor object by the same name in the class.")
pos3 = PositionX(50)
print(pos3.x)
pos3.x = 100
print(pos3.x)
try:
pos3.x = -10
except ValueError:
print("Can't set x to -10")
print(pos3.x)
print()
# Let's define __set__ and __get__ to validate properties like x
# (with the range 0 to 1000). This actually makes the class Range0to1000
# a data descriptor. The instance dictionary of the managed class PositionX
# is always overrided by the descriptor.
# This works because now on x attribute reads and writes of a PositionX
# instance the __get__ or __set__ descriptor methods are always run.
# When run they get or set the PositionX instance __dict__ to bypass the
# trigger of descriptor __get__ or __set__ (again)
class Range0to1000:
def __init__(self, name): # the property name, 'x', 'y', whatever
self.name = name
self.value = None
def __get__(self, instance, managed_class):
print('Running __get__')
return instance.__dict__[self.name]
# same as getattr(instance, self.name) but doesn't trigger
# another call to __get__ leading to recursion error
def __set__(self, instance, value):
print('Running __set__')
if 0 <= value <= 1000:
instance.__dict__[self.name] = value
# same as setattr(instance, self.name, self.value) but doesn't
# trigger another call to __set__ leading to recursion error
else:
raise ValueError
class PositionX: # holds a x attribute from 0 to 1000
x = Range0to1000('x') # no easy way to avoid passing the name string 'x'
# but now you can add many other properties
# sharing the same validation code
# y = Range0to1000('y')
# ...
def __init__(self, x):
self.x = x
print("Using a descriptor class to validate x.")
pos4 = PositionX(50)
print(pos4.x)
pos4.x = 100
print(pos4.x)
try:
pos4.x = -10
except ValueError:
print("Can't set x to -10")
print(pos4.x)
print()
print('***End***')
It's unclear to me how one accomplishes this in Python, though I am confused about the fundamentals of OOP:
Let's say I create a Shape class, and use this as an example.
class Shape:
def __init__(self, x, y):
self.x = x
self.y = y
def area(self):
return self.x * self.y
def perimeter(self):
return 2 * self.x + 2 * self.y
def scaleSize(self,scale):
self.x = self.x * scale
self.y = self.y * scale
In my case, I must create a Class which inputs a file whereby there are only two types, file_typeA and file_typeB. For the user, it would be exceptionally cumbersome to use a Class for Type A and a Class for Type B.
## use case for an a "read in file name" class `filename`
foo = filename(path = "path/to/file.txt", file_type = "TypeA")
My question is, how do I restrict the "Type A" methods for a file initialized with flag file_type="TypeA", and restrict the methods for a file initialized with flag file_type = "TypeB"?
Let's try the Shape class again, whereby the two types are Square and Triangle. The wrong way to code this is something like this:
class Shape:
def __init__(self, x, y, type):
self.x = x
self.y = y
self.type = type
if type == "Square":
def area(self):
return self.x * self.y
def perimeter(self):
return 2 * self.x + 2 * self.y
def scaleSize(self,scale):
self.x = self.x * scale
self.y = self.y * scale
elif type == "Triangle":
def hooray(self):
print("HOORAY! It's hip to be a triangle!")
else:
print("'type' must be either Circle or Triangle")
## example Class instantiation
bar = Shape(2, 3, type="Square")
For such a class, how do I create it such that the method area cannot be used for a Shape of type=="TypeB"?
I think you need to define a mother class:
class Shape:
pass
then sub-classes
class Triangle(Shape):
# whatever methods there are
What you're describing is a factory function which creates the proper object according to some parameter:
def create(type):
if type=="Triangle":
return Triangle()
elif type=="Square":
return Square()
but it's not very useful since the caller has to pass the name of the class instead:
c = create("Triangle")
instead of just doing:
c = Triangle()
Well, it may have its interest when saving/restoring a program state from a text file containing the type(s) of the objects in the program memory when saved (if you don't want to use pickle).
That's not how you OOP! OOP helps to eliminate those kinds of conditionals. You should create a Shape base class, then use that to define subclasses for each individual kind of shape. The subclasses should include the methods needed for that kind of shape.
If you want the caller to be able to specify a shape using a string, for example because they are using data from a file or the user, you can write a factory method to do that. You can actually ask Python for the subclasses of your class; no need to list them out! DRY
class Shape(object):
__subs = {}
#classmethod
def of_type(cls, name, *args, **kwargs):
"""Factory method that instantiates Shape subclasses given their name as a string"""
lname = name.lower()
subs = cls._Shape__subs
if cls is Shape:
if not subs: # build subclass dictionary
subs.update({c.__name__.lower(): c for c in cls.__subclasses__()})
if lname in subs: # instantiate the named subclass
return subs[lname](*args, **kwargs)
raise NameError("invalid shape name: %s" % name)
raise TypeError("of_type() may be called only on Shape base class")
class Square(Shape):
pass
class Triangle(Shape):
pass
# set tri to an instance of type Triangle
tri = Shape.of_type("Triangle")
Note that the factory method passes through any arguments given after the name. Class names are case-insensitive.