How to use method parameter as parameter in another method? - python-3.x

class Class1(object):
def __init__(self, parameter1):
# action with parameter
def method1(self, parameter1):
# method actions
So what I want to happen is that I am able to make a Class1 object without having loaded the parameter1 yet and then when that has happened, I use method1 to set parameter1 and run actions with method1 as __init__ will use the results of method1. This is a python tutorial practice exam by the way so it has to be done this way.
EDIT:
>>>object1 = Class1()
>>>object1.method1(parameter1)

In order to allow a later initialization, you want to move all your actual initialization stuff into the method and make the parameter to the __init__ optional. Then, if the parameter is specified, you can call the method or not.
class SomeClass (object):
def __init__ (self, param = None):
# do some general initialization, like initializing instance members
self.foo = 'bar'
# if the parameter is specified, call the init method
if param is not None:
self.init(param)
def init (self, param):
# do initialization stuff
Then, both of the following ways to create the object are equivalent:
x = SomeClass('param value')
y = SomeClass()
y.init('param value')

If the idea is to be able to assign a value for the attribute at the method level and not in the initialization of the Class, I would suggest the following implementation:
class Class:
def __init__(self, parameter=None):
self.parameter=parameter
def method(self, parameter):
self.parameter = parameter
You can check that the attribute is certainly assigned through the method:
>>> c = Class1()
>>> c.method('whatever')
>>> print(c.parameter)
whatever
BTW in Python3 you don't need to explicitly inherit from object anymore, since already "all classes inherit from object".

Related

Require overriding method to call super()

I'd like to force certain methods in child classes to call to invoke the method they're overriding.
#abstractmethod can require certain methods be implemented; I'd like a behavior similar to this (i.e., if the overriding method doesn't call super(), don't execute and complain to the user).
Example:
class Foo:
#must_call_super
def i_do_things(self):
print('called')
class Good(Foo):
def i_do_things(self):
# super().i_do_things() is called; will run.
super().i_do_things()
print('called as well')
class Bad(Foo):
def i_do_things(self):
# should complain that super().i_do_things isn't called here
print('called as well')
# should work fine
good = Good()
# should error
bad = Bad()
Thanks for sending me down the rabbit hole.
Below is my solution to this problem. It uses metaclass, ast, and some hacking to detect whether a child class calls super().some_func() in its version of some_func method.
Core classes
These should be controlled by the developer.
import inspect
import ast
import textwrap
class Analyzer(ast.NodeVisitor):
def __init__(self, ast_sig: str):
self.func_exists = False
self.sig = ast_sig
def visit_Call(self, node):
"""Traverse the ast tree. Once a node's signature matches the given
method call's signature, we consider that the method call exists.
"""
# print(ast.dump(node))
if ast.dump(node) == self.sig:
self.func_exists |= True
self.generic_visit(node)
class FooMeta(type):
# _ast_sig_super_methods stores the ast signature of any method that
# a `super().method()` call must be made in its overridden version in an
# inherited child. One can add more method and its associted ast sig in
# this dict.
_ast_sig_super_methods = {
'i_do_things': "Call(func=Attribute(value=Call(func=Name(id='super', ctx=Load()), args=[], keywords=[]), attr='i_do_things', ctx=Load()), args=[], keywords=[])",
}
def __new__(cls, name, bases, dct):
# cls = FooMeta
# name = current class name
# bases = any parents of the current class
# dct = namespace dict of the current class
for method, ast_sig in FooMeta._ast_sig_super_methods.items():
if name != 'Foo' and method in dct: # desired method in subclass
source = inspect.getsource(dct[method]) # get source code
formatted_source = textwrap.dedent(source) # correct indentation
tree = ast.parse(formatted_source) # obtain ast tree
analyzer = Analyzer(ast_sig)
analyzer.visit(tree)
if not analyzer.func_exists:
raise RuntimeError(f'super().{method} is not called in {name}.{method}!')
return super().__new__(cls, name, bases, dct)
class Foo(metaclass=FooMeta):
def i_do_things(self):
print('called')
Usage and Effect
This is done by other people, from whom we want to dictate that super().i_do_things must be called in the overridden version in their inherited classes.
Good
class Good(Foo):
def i_do_things(self):
# super().i_do_things() is called; will run.
super().i_do_things()
print('called as well')
good = Good()
good.i_do_things()
# output:
# called
# called as well
Bad
class Bad(Foo):
def i_do_things(self):
# should complain that super().i_do_things isn't called here
print('called as well')
# Error output:
# RuntimeError: super().i_do_things is not called in Bad.i_do_things!
Secretly Bad
class Good(Foo):
def i_do_things(self):
# super().i_do_things() is called; will run.
super().i_do_things()
print('called as well')
class SecretlyBad(Good):
def i_do_things(self):
# also shall complain super().i_do_things isn't called
print('called as well')
# Error output:
# RuntimeError: super().i_do_things is not called in SecretlyBad.i_do_things!
Note
Since FooMeta is executed when the inherited classes are defined, not when they are instantiated, error is thrown before Bad().i_do_things() or SecretlyBad().i_do_things() is called. This is not the same as the requirement by the OP, but it does achieve the same end goal.
To obtain the ast signature of super().i_do_things(), we can uncomment the print statement in Analyzer, analyze the source code of Good.i_do_things, and inspect from there.

Python how can I patch a classmethod so I can access the cls variable

I am interested in patching the a classmethod called _validate in a Schema class and in a replaced fn using the value of cls and the other arguments.
For context ArrayHoldingAnyType inherits from Schema and _validate is called when it is instantiated.
When I try it with the below code, the value for cls is not a class. How do I fix the cls variable?
def test_validate_called_n_times(self):
def replacement_validate(cls, *args):
# code which will return the correct values
with patch.object(Schema, '_validate', new=replacement_validate) as mock_validate:
path_to_schemas = ArrayHoldingAnyType(['a'])
# I will check that the mock was called a certain number of times here with specific inputs
So the problem here was that the classmethod decorator was missing from replacement_validate.
This fixes it:
def test_validate_called_n_times(self):
#classmethod
def replacement_validate(cls, *args):
# code which will return the correct values
with patch.object(Schema, '_validate', new=replacement_validate) as mock_validate:
path_to_schemas = ArrayHoldingAnyType(['a'])
# I will check that the mock was called a certain number of times here with specific inputs

MetaClass in Python

I am trying to create a Meta-Class for my Class.
I have tried to print information about my class in meta-class
Now I have created two objects of my class
But Second object gets created without referencing my Meta-Class
Does Meta Class gets called only once per Class??
Any help will be appreciated
Thanks
class Singleton(type):
def __new__(cls,name,bases,attr):
print (f"name {name}")
print (f"bases {bases}")
print (f"attr {attr}")
print ("Space Please")
return super(Singleton,cls).__new__(cls,name,bases,attr)
class Multiply(metaclass = Singleton):
pass
objA = Multiply()
objB = Multiply()
print (objA)
print (objB)
Yes - the metaclass's __new__ and __init__ methods are called only when the class is created. After that, in your example, the class will be bound to theMultiply name. In many aspects, it is just an object like any other in Python. When you do objA = Multiply() you are not creating a new instance of type(Multiply), which is the metaclass - you are creating a new instance of Multiply itself: Multiply.__new__ and Multiply.__init__ are called.
Now, there is this: the mechanism in Python which make __new__ and __init__ be called when creating an instance is the code in the metaclass __call__ method. That is, just as when you create any class with a __call__ method and use an instance of it with the calling syntax obj() will invoke type(obj).__call__(obj), when you do Multiply() what is called (in this case) is Singleton.__call__(Multiply).
Since it is not implemented, Singleton's superclass, which is type __call__ method is called instead - and it is in there that Multiply.__new__ and __init__ are called.
That said, there is nothing in the code above that would make your classes behave as "singletons". And more importantly you don't need a metaclass to have a singleton in Python. I don't know who invented this thing, but it keeps circulating around.
First, if you really need a singleton, all you need to do is to write a plain class, no special anything, create your single instance, and document that the instance should be used. Just as people use None - no one keeps getting a reference to Nonetype and keep calling it to get a None reference:
class _Multiply:
...
# document that the code should use this instance:
Multiply = _Multiply()
second Alternatively, if your code have a need, whatsoever, for instantiating the class that should be a singleton where it will be used, you can use the class' __new__ method itself to control instantiation, no need for a metaclass:
class Multiply:
_instance = None
def __new__(cls):
if not cls._instance:
cls._instance = super().__new__(cls)
# insert any code that would go in `__init__` here:
...
...
return cls._instance
Third just for demonstration purposes, please don't use this, the metaclass mechanism to have singletons can be built in the __call__ method:
class Singleton(type):
registry = {}
def __new__(mcls,name,bases,attr):
print(f"name {name}")
print(f"bases {bases}")
print(f"attr {attr}")
print("Class created")
print ("Space Please")
return super(Singleton,mcls).__new__(cls,name,bases,attr)
def __call__(cls, *args, **kw):
registry = type(cls).registry
if cls not in registry:
print(f"{cls.__name__} being instantiated for the first time")
registry[cls] = super().__call__(*args, **kw)
else:
print(f"Attempting to create a new instance of {cls.__name__}. Returning single instance instead")
return registry[cls]
class Multiply(metaclass = Singleton):
pass

how to use python decorator with argument?

I would like to define a decorator that will register classes by a name given as an argument of my decorator. I could read from stackoverflow and other sources many examples that show how to derive such (tricky) code but when adapted to my needs my code fails to produce the expected result. Here is the code:
import functools
READERS = {}
def register(typ):
def decorator_register(kls):
#functools.wraps(kls)
def wrapper_register(*args, **kwargs):
READERS[typ] = kls
return wrapper_register
return decorator_register
#register(".pdb")
class PDBReader:
pass
#register(".gro")
class GromacsReader:
pass
print(READERS)
This code produces an empty dictionary while I would expect a dictionary with two entries. Would you have any idea about what is wrong with my code ?
Taking arguments (via (...)) and decoration (via #) both result in calls of functions. Each "stage" of taking arguments or decoration maps to one call and thus one nested functions in the decorator definition. register is a three-stage decorator and takes as many calls to trigger its innermost code. Of these,
the first is the argument ((".pdb")),
the second is the class definition (#... class), and
the third is the class call/instantiation (PDBReader(...))
This stage is broken as it does not instantiate the class.
In order to store the class itself in the dictionary, store it at the second stage. As the instances are not to be stored, remove the third stage.
def register(typ): # first stage: file extension
"""Create a decorator to register its target for the given `typ`"""
def decorator_register(kls): # second stage: Reader class
"""Decorator to register its target `kls` for the previously given `typ`"""
READERS[typ] = kls
return kls # <<< return class to preserve it
return decorator_register
Take note that the result of a decorator replaces its target. Thus, you should generally return the target itself or an equivalent object. Since in this case the class is returned immediately, there is no need to use functools.wraps.
READERS = {}
def register(typ): # first stage: file extension
"""Create a decorator to register its target for the given `typ`"""
def decorator_register(kls): # second stage: Reader class
"""Decorator to register its target `kls` for the previously given `typ`"""
READERS[typ] = kls
return kls # <<< return class to preserve it
return decorator_register
#register(".pdb")
class PDBReader:
pass
#register(".gro")
class GromacsReader:
pass
print(READERS) # {'.pdb': <class '__main__.PDBReader'>, '.gro': <class '__main__.GromacsReader'>}
If you don't actually call the code that the decorator is "wrapping" then the "inner" function will not fire, and you will not create an entry inside of READER. However, even if you create instances of PDBReader or GromacsReader, the value inside of READER will be of the classes themselves, not an instance of them.
If you want to do the latter, you have to change wrapper_register to something like this:
def register(typ):
def decorator_register(kls):
#functools.wraps(kls)
def wrapper_register(*args, **kwargs):
READERS[typ] = kls(*args, **kwargs)
return READERS[typ]
return wrapper_register
return decorator_register
I added simple init/repr inside of the classes to visualize it better:
#register(".pdb")
class PDBReader:
def __init__(self, var):
self.var = var
def __repr__(self):
return f"PDBReader({self.var})"
#register(".gro")
class GromacsReader:
def __init__(self, var):
self.var = var
def __repr__(self):
return f"GromacsReader({self.var})"
And then we initialize some objects:
x = PDBReader("Inside of PDB")
z = GromacsReader("Inside of Gromacs")
print(x) # Output: PDBReader(Inside of PDB)
print(z) # Output: GromacsReader(Inside of Gromacs)
print(READERS) # Output: {'.pdb': PDBReader(Inside of PDB), '.gro': GromacsReader(Inside of Gromacs)}
If you don't want to store the initialized object in READER however, you will still need to return an initialized object, otherwise when you try to initialize the object, it will return None.
You can then simply change wrapper_register to:
def wrapper_register(*args, **kwargs):
READERS[typ] = kls
return kls(*args, **kwargs)

How do I call outside function from class in python

How do i call an outside function from this class ?
def test(t):
return t
class class_test():
def test_def(q):
test_msg = test('Hi')
print (test_msg)
To call the class method, you can create an instance of the class and then call an attribute of that instance (the test_def method).
def test(t):
return t
class ClassTest(object):
def test_def(self):
msg = test('Hi')
print(msg)
# Creates new instance.
my_new_instance = ClassTest()
# Calls its attribute.
my_new_instance.test_def()
Alternatively you can call it this way:
ClassTest().test_def()
Sidenote: I made a few changes to your code. self should be used as first argument of class methods when you define them. object should be used in a similar manner.

Resources