__lt__ and print operator overload - python

I have this assignment in which I have to define a class named Person with attributes name, surname and age. I have done getter and setter methods; now I have an issue with overloading operators.
First, I need to overload a print operator (which I have done); second, I need to overload "less than" operator which gives me the following error:
TypeError: '<' not supported between instances of 'Person' and 'Person'
And in the last step, I need to compare the ages of different persons e.g.:
Sabine=Person("Sabine","Musterfrau",17)
Anton_Junior=Person("Anton","Mueller",14)
print(Sabine < Anton_Junior) should return false and vice versa
My problem is: 1. the type error and 2. I have already overloaded print method and they want me to use the default print() later.
Here is my code:
from sys import stdout
class Person:
def __init__(self,vorname,nachname,alter):
self.vorname=vorname
self.nachname=nachname
self._alter=alter
def get_Alter(self):
return self._alter
def set_Alter(self,alter2):
self._alter=alter2
def print(person):
stdout.write("Name:"+person.vorname+" Nachname:"+person.nachname+" Alter:"+str(person._alter)+"\n")
def __lt__(self,other):
return self._alter() < other._alter()
Sabine=Person("Sabine","Musterfrau",17)
Sabine.set_Alter(18)
Anton_Junior=Person("Anton","Mueller",14)
Anton_Senior=Person("Anton","Mueller",80)
print(Sabine < Anton_Junior)
print(Sabine)
Ok, just finished my task, thank you all!!!

First, self._alter is a field/property/value (name it whatever you want) and not method. This should help you:
def __lt__(self,other):
return self._alter < other._alter
When it comes to second problem::
I have already overloaded print method and they want me to use the default print() later.
Refefine __str__() method.
>>> class MyClass:
... def __str__(self):
... return 'This is my class'
...
>>> mc = MyClass()
>>> print(mc)
This is my class

Related

In Python can isinstance() be used to detect a class method?

How to determine if an object is a class method? Isn't it best practice to use isinstance(), and how does one make that work?
class Foo:
class_var = 0
#classmethod
def bar(cls):
cls.class_var += 1
print("class variable value:", cls.class_var)
def wrapper(wrapped: classmethod):
"""
Call the wrapped method.
:param wrapped (classmethod, required)
"""
wrapped()
Foo.bar()
wrapper(Foo.bar)
print("the type is:", type(Foo.bar))
print("instance check success:", isinstance(Foo.bar, classmethod))
Output:
class variable value: 1
class variable value: 2
the type is: <class 'method'>
instance check success: False
Process finished with exit code 0
If you just want to tell class methods apart from regular methods and static methods, then you can check this with inspect.ismethod(f).
class A:
def method(self): pass
#classmethod
def class_method(cls): pass
#staticmethod
def static_method(): pass
In the REPL:
>>> from inspect import ismethod
>>> ismethod(A.method)
False
>>> ismethod(A.class_method)
True
>>> ismethod(A.static_method)
False
If you prefer to do this with isinstance, then that's possible using typing.types.MethodType:
>>> from typing import types
>>> isinstance(A.method, types.MethodType)
False
>>> isinstance(A.class_method, types.MethodType)
True
>>> isinstance(A.static_method, types.MethodType)
False
Note that these tests will incorrectly identify e.g. A().method because really we're just testing for a bound method as opposed to an unbound function. So the above solutions only work assuming that you are checking A.something where A is a class and something is either a regular method, a class method or a static method.
As you know Python fills the first parameter of the classmethods with a reference to the class itself and it doesn't matter if you call that method from the class or the instance of the class. A method object is a function which has an object bound to it.
That object can be retrieved by .__self__ attribute. So you can simply check that if the .__self__ attribute is a class or not. If it is a class , it's class is type.
One way of doing it:
class Foo:
#classmethod
def fn1(cls):
pass
def fn2(self):
pass
def is_classmethod(m):
first_parameter = getattr(m, '__self__', None)
if not first_parameter:
return False
type_ = type(first_parameter)
return type_ is type
print(is_classmethod(Foo.fn1))
print(is_classmethod(Foo().fn1))
print("-----------------------------------")
print(is_classmethod(Foo.fn2))
print(is_classmethod(Foo().fn2))
output:
True
True
-----------------------------------
False
False
There is a ismethod function in inspect module that specifically checks that if the object is a bound method. You can use this as well before checking for the type of the first parameter.
NOTE: There is a caveat with the above solution, I'll mention it at the end.
Solution number 2:
Your isinstance solution didn't work because classmethod is a descriptor. If you want to get the actual classmethod instance, you should check the Foo's namespace and get the methods from there.
class Foo:
#classmethod
def fn1(cls):
pass
def fn2(self):
pass
def is_classmethod(cls, m):
return isinstance(cls.__dict__[m.__name__], classmethod)
print(is_classmethod(Foo, Foo.fn1))
print(is_classmethod(Foo, Foo().fn1))
print("-----------------------------------")
print(is_classmethod(Foo, Foo.fn2))
print(is_classmethod(Foo, Foo().fn2))
Solution number 1 caveat: For example if you have a simple MethodType object whose bound object is a different class like int here, this solution isn't going to work. Because remember we just checked that if the first parameter is of type type:
from types import MethodType
class Foo:
def fn2(self):
pass
fn2 = MethodType(fn2, int)
#classmethod
def fn1(cls):
pass
Now only solution number 2 works.

Is there a way in a class function to return an instance of the class itself?

I have a class in python with a function and I need that function to explicitly return an instance of that class. I tried this
class a(type):
def __init__(self, n):
self.n = n
def foo() -> a:
return a(self.n + 1)
but I get an error "a is not defined". What should I do? Thanks.
Since OP used annotation in member function. There is a NameError in the annotation also. To fix that. Try following:
Reference:
https://www.python.org/dev/peps/pep-0484/#id34
Annotating instance and class methods
In most cases the first argument of class and instance methods does
not need to be annotated, and it is assumed to have the type of the
containing class for instance methods, and a type object type
corresponding to the containing class object for class methods. In
addition, the first argument in an instance method can be annotated
with a type variable. In this case the return type may use the same
type variable, thus making that method a generic function.
from typing import TypeVar
T = TypeVar('T', bound='a')
class a:
def __init__(self: T, n: int):
self.n = n
def foo(self: T) -> T:
return a(self.n + 1)
print(a(1).foo().n)
Result:
2
What you are asking works:
class A:
def __init__(self, n):
self.n = n
def foo(self):
return A(self.n + 1)
a = A(1)
b = a.foo()
print(a.n, b.n)
There are sevaral problems with your original code though.
The type hint -> A does not work because A is not defined at that point.
You need to pass self to the foo method as well.
If you subclass type, and want to make use of its features, I suggest you also initialize it by calling super().__init__() and pass on all necessary arguments. You can do that at any point you prefer, but usually it's done in the __init__() method of the subclass.

How to add a constructor to a subclassed numeric type?

I want to subclass a numeric type (say, int) in python and give it a shiny complex constructor. Something like this:
class NamedInteger(int):
def __init__(self, value):
super(NamedInteger, self).__init__(value)
self.name = 'pony'
def __str__(self):
return self.name
x = NamedInteger(5)
print x + 3
print str(x)
This works fine under Python 2.4, but Python 2.6 gives a deprecation warning. What is the best way to subclass a numeric type and to redefine constructors for builtin types in newer Python versions?
Edit:
Spotted in comments that this works without a super() line, so it could be like this:
class NamedInteger(int):
def __init__(self, value):
self.name = 'pony'
def __str__(self):
return self.name
x = NamedInteger(5)
print x + 3
print str(x)
I believe that this works because int is immutable type and has only __new__ method. However I would be glad to know a correct way of subclassing, so I could build a class with behaviour like this:
x = NamedInteger(5, 'kitty')
Second edit:
The final version now looks like this:
class NamedInteger(int):
def __new__(cls, value, name='pony'):
self = super(NamedInteger, cls).__new__(cls, value)
self.name = name
return self
def __str__(self):
return self.name
x = NamedInteger(5)
y = NamedInteger(3, 'kitty')
print "%d %d" % (x, y)
print "%s %s" % (x, y)
Answers below also gave very interesting links to Abstract Base Classes and numbers modules.
You have to use __new__ instead of __init__ when you subclass immutable built-in types, e.g. :
class NamedInteger(int):
def __new__(cls, value, name='pony'):
inst = super(NamedInteger, cls).__new__(cls, value)
inst.name = name
return inst
def __str__(self):
return self.name
x = NamedInteger(5)
print x + 3 # => 8
print str(x) # => pony
x = NamedInteger(3, "foo")
print x + 3 # => 6
print str(x) # => foo
As of Python 2.6, the preferred way to extend numeric types is not to directly inherit from them, but rather to register your class as a subclass of the Number abstract base class. Check out the abc module for documentation of the Abstract Base Class concept.
That module's documentation links to the numbers module, which contains the abstract base classes you can choose to declare yourself part of. So basically you'd say
import numbers
numbers.Number.register(NamedInteger)
to indicate that your class is a type of number.
Of course, the problem with this is that it requires you to implement all the various handler methods such as __add__, __mul__, etc. However, you'd really have to do this anyway, since you can't rely on the int class' implementation of those operations to do the correct thing for your class. For example, what's supposed to happen when you add an integer to a named integer?
My understanding is that the ABC approach is intended to force you to confront those questions. In this case the simplest thing to do is probably to keep an int as an instance variable of your class; in other words while you will register your class to give it the is-a relationship with Number, your implementation gives it a has-a relationship with int.
It will work fine if you don't pass value to super(NamedInteger, self).__init__()
I wonder why though, I'm learning from your post :-)

What does __contains__ do, what can call __contains__ function

Here is my code:
class a(object):
d='ddd'
def __contains__(self):
if self.d:return True
b=a()
print b.contains('d') # error
print contains(b,'d') # error
Like all special methods (with "magic names" that begin and end in __), __contains__ is not meant to be called directly (except in very specific cases, such as up=calls to the superclass): rather, such methods are called as part of the operation of built-ins and operators. In the case of __contains__, the operator in question is in -- the "containment check" operator.
With your class a as you present it (except for fixing your typo, and using True instead of true!-), and b as its instance, print 'x' in b will print True -- and so will any other containment check on b, since b always returns True (because self.d, a non-empty string, is true).
to get your code to do something (although nothing useful):
class a(object):
d = 'ddd'
def __contains__(self, m):
if self.d:
return True
b = a()
>>> 'd' in b
True
The docs.
__contains__ method defines how instances of class behave when they appear at right side of in and not in operator.
class Person(object):
def __init__(self,name,age):
self.name = name
self.age = age
def __contains__(self,param1):
return True if param1 in self.__dict__.keys() else False
>>> p = Person('Robby Krieger',23)
>>> 'name' in p
True
Lets see a very simple example of magic method __contains__ :
Suppose I have class Player and my __init__ method takes one string argument name. In main I have created an object (obj1) of class Player.
Now if I want to know if my obj1 (in this case attribute name of obj1) contains a particular string, substring or an alphabet, I have to implement __contains__ method as shown in the example.
If my class has __contains__ method I can call built-in operator in on my custom objects as shown in the example.
class Player():
def __init__(self, name):
self.name=name
def __contains__(self, substring):
if substring in self.name:
return True
else:
return False
obj1=Player("Sam")
print ('am' in obj1) ----> True
print ('ami' in obj1) ----> False
if self.d:return true
self.d is the string 'ddd'. Non-empty strings are always truthy: when you use if on 'ddd' it will always act as if you'd said if True:.
I think what you probably meant is:
def __contains__(self, item):
return item in self.d
in is the operator that calls the __contains__ method behind the scenes.

Python Class with integer emulation

Given is the following example:
class Foo(object):
def __init__(self, value=0):
self.value=value
def __int__(self):
return self.value
I want to have a class Foo, which acts as an integer (or float). So I want to do the following things:
f=Foo(3)
print int(f)+5 # is working
print f+5 # TypeError: unsupported operand type(s) for +: 'Foo' and 'int'
The first statement print int(f)+5 is working, cause there are two integers. The second one is failing, because I have to implement __add__ to do this operation with my class.
So to implement the integer behaviour, I have to implement all the integer emulating methods. How could I get around this. I tried to inherit from int, but this attempt was not successful.
Update
Inheriting from int fails, if you want to use a __init__:
class Foo(int):
def __init__(self, some_argument=None, value=0):
self.value=value
# do some stuff
def __int__(self):
return int(self.value)
If you then call:
f=Foo(some_argument=3)
you get:
TypeError: 'some_argument' is an invalid keyword argument for this function
Tested with Python 2.5 and 2.6
In Python 2.4+ inheriting from int works:
class MyInt(int):pass
f=MyInt(3)
assert f + 5 == 8
You need to override __new__, not __init__:
class Foo(int):
def __new__(cls, some_argument=None, value=0):
i = int.__new__(cls, value)
i._some_argument = some_argument
return i
def print_some_argument(self):
print self._some_argument
Now your class work as expected:
>>> f = Foo(some_argument="I am a customized int", value=10)
>>> f
10
>>> f + 8
18
>>> f * 0.25
2.5
>>> f.print_some_argument()
I am a customized int
More information about overriding new can be found in Unifying types and classes in Python 2.2.
Try to use an up-to-date version of python. Your code works in 2.6.1.

Categories