For understanding decorators in Python, i created in a class an example. But when i run it i receive an error.
class Operation:
def __init__(self, groupe):
self.__groupe = groupe
#property
def groupe(self):
return self.__groupe
#groupe.setter
def groupe(self, value):
self.__groupe = value
def addition(self, func_goodbye):
ln_house = len('house')
ln_school = len('school')
add = ln_house + ln_school
print('The result is :' + str(add))
return func_goodbye
#addition
def goodbye(self):
print('Goodbye people !!')
if __name__ == '__main__':
p1 = Operation('Student')
p1.goodbye()
I receive this error :
Traceback (most recent call last):
File "Operation.py", line 1, in
class Operation:
File "Operation.py", line 21, in Operation
#addition
TypeError: addition() missing 1 required positional argument: 'func_goodbye'
You can have a class scoped decorator, however there won't be a self when the decorator is called
a decorator:
#foo
def bar(): ...
is roughly equivalent to
def bar(): ...
bar = foo(bar)
in your particular example, if you remove the self parameter, it should function as you expect:
def addition(func_goodbye):
ln_house = len('house')
ln_school = len('school')
add = ln_house + ln_school
print('The result is :' + str(add))
return func_goodbye
#addition
def goodbye(self):
print('Goodbye people !!')
for good measure, I might del addition after that just to ensure it isn't accidentally called later
(an aside: one unfortunate side-effect of this is many linters and type checkers will consider this "odd" so I've yet to find a way to appease them (for example mypy))
Related
i'm trying to solve the adventofcode riddles, but this year i decided to take the chance to learn a new programming language: Python.
Since i already have some knowledge about other OOP languages like Java and C++, i immediately created an "interface" system for the Solutions objects.
My project setup at the moment is like:
Project Setup
Now what i want is to dynamically output solutions through the main class, that basically has to call all .solve() method of each dayX.py class that is in the /solutions/ directory.
I think i'm next to do it but i get an error:
Traceback (most recent call last):
File "C:\Users\siste\j-workspace\adventofcode2020\main.py", line 16, in <module>
print(solution.solve())
TypeError: solve() missing 1 required positional argument: 'self'
Here are my files:
main.py
import os
def days():
classes = []
path = "C:\\path"
for file in os.listdir(path):
if(file.startswith("day")) :
classes.append(str(file.replace(".py", "")))
return classes
if __name__ == '__main__':
for day in days() :
solution = getattr(__import__(str("solutions." + day)), day.replace("d", "D"))
print(solution.solve())
solution.py
path = "C:\\path\\inputs\\day{}.txt"
class Solution:
def __init__(self, dayNumber):
self.dayNumber = dayNumber
self.inputPath = path.format(self.dayNumber)
def part1(self):
pass
def part2(self):
pass
def solve(self):
return ("Day {} Solutions: \n\tPart1: {}\n\tPart2: {}"
.format(self.dayNumber, self.part1(), self.part2()))
day1.py
import fileinput
from solutions.solution import Solution
class Day1(Solution):
def __init__(self):
super().__init__(1)
def part1(self):
return "sol"
def part2(self):
return "sol2"
When you're using the getattr on the imported module, you're getting the class definition. Methods are only callable on class instances, otherwise they throw the error you're seeing:
class A:
def a(self):
pass
A.a() // Will throw "TypeError: a() missing 1 required positional argument: 'self'"
A().a() // OK
Changing the __main__ part this way should solve the issue:
if __name__ == '__main__':
for day in days() :
day_class = getattr(__import__(str("solutions." + day)), day.replace("d", "D"))
day_instance = day_class()
print(day_instance.solve())
I'm trying to pass variable list in Class and Function in object oriented manner but facing some error, I don't understand what wrong with it and I'm using PyDev in Eclipse
CODE
class Mydetails:
__details = ''
def __init__(self,details): # constructor
#self.__details['country'] = details['country']
self.__details = details
def set_element(self,details):
self.__details = details
def get_list(self,details):
return self.__details
details = ['ABC','DEF','GHI','JKL']
pdetails = Mydetails()
pdetails.set_element(details)
print(pdetails.get_list())
OUTPUT
Traceback (most recent call last):
File "C:\workspace\python\pythonapp\list.py", line 18, in <module>
pdetails = Mydetails()
TypeError: __init__() missing 1 required positional argument: 'details'
in this line:
def __init__(self,details):
you made the init function take a parameter (details), but here:
pdetails = Mydetails()
you arent giving it a parameter! what you probably wanted to do there is:
pdetails = Mydetails(details)
on a side note your def get_list(self,details): function doesn't make sense, why are you passing it a parameter that doesn't get used?
I fixed my code ,It was error in function def get_list(self,details): changed to def get_details(self): because details parameter already copied in self so it not allow the same thats why throwing error, but below code is fixed and work fine with function calling and defined object also correct from previous code.
CODE
class Mydetails:
__details = '' #protected
def __init__(self,details={}): # constructor
self.__details = details
def set_details(self,details): # function
self.__details = details
def get_details(self):
return self.__details
def toString(self):
return 'Welcome {}'.format(self.__details)
new_details = ['xyz','klm','nop']
pdetails = Mydetails()
pdetails.set_details(new_details)
print(pdetails.get_details())
OUTPUT
['xyz', 'klm', 'nop']
I want to write a decorator that inject custom local variable into function.
interface may like this.
def enclose(name, value):
...
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
expectation:
#enclose('param1', 1)
def f():
param1 += 1
print param1
f() will compile and run without error
output:
2
Is it possible to do this in python? why?
I thought I'd try this out just to see how hard it would be. Pretty hard as it turns out.
First thing was how do you implement this? Is the extra parameter an injected local variable, an additional argument to the function or a nonlocal variable. An injected local variable will be a fresh object each time, but how to create more complicated objects... An additional argument will record mutations to the object, but assignments to the name will be forgotten between function invocations. Additionally, this will require either parsing of the source to find where to place the argument, or directly manipulating code objects. Finally, declaring the variables nonlocal will record mutations to the object and assignments to the name. Effectively a nonlocal is global, but only reachable by the decorated function. Again, using a nonlocal will requiring parsing the source and finding where to place the nonlocal declaration or direct manipulation of a code object.
In the end I decided with using a nonlocal variable and parsing the function source. Originally I was going to manipulate code objects, but it seemed too complicated.
Here is the code for the decorator:
import re
import types
import inspect
class DummyInject:
def __call__(self, **kwargs):
return lambda func: func
def __getattr__(self, name):
return self
class Inject:
function_end = re.compile(r"\)\s*:\s*\n")
indent = re.compile("\s+")
decorator = re.compile("#([a-zA-Z0-9_]+)[.a-zA-Z0-9_]*")
exec_source = """
def create_new_func({closure_names}):
{func_source}
{indent}return {func_name}"""
nonlocal_declaration = "{indent}nonlocal {closure_names};"
def __init__(self, **closure_vars):
self.closure_vars = closure_vars
def __call__(self, func):
lines, line_number = inspect.getsourcelines(func)
self.inject_nonlocal_declaration(lines)
new_func = self.create_new_function(lines, func)
return new_func
def inject_nonlocal_declaration(self, lines):
"""hides nonlocal declaration in first line of function."""
function_body_start = self.get_function_body_start(lines)
nonlocals = self.nonlocal_declaration.format(
indent=self.indent.match(lines[function_body_start]).group(),
closure_names=", ".join(self.closure_vars)
)
lines[function_body_start] = nonlocals + lines[function_body_start]
return lines
def get_function_body_start(self, lines):
line_iter = enumerate(lines)
found_function_header = False
for i, line in line_iter:
if self.function_end.search(line):
found_function_header = True
break
assert found_function_header
for i, line in line_iter:
if not line.strip().startswith("#"):
break
return i
def create_new_function(self, lines, func):
# prepares source -- eg. making sure indenting is correct
declaration_indent, body_indent = self.get_indent(lines)
if not declaration_indent:
lines = [body_indent + line for line in lines]
exec_code = self.exec_source.format(
closure_names=", ".join(self.closure_vars),
func_source="".join(lines),
indent=declaration_indent if declaration_indent else body_indent,
func_name=func.__name__
)
# create new func -- mainly only want code object contained by new func
lvars = {"closure_vars": self.closure_vars}
gvars = self.get_decorators(exec_code, func.__globals__)
exec(exec_code, gvars, lvars)
new_func = eval("create_new_func(**closure_vars)", gvars, lvars)
# add back bits that enable function to work well
# includes original global references and
new_func = self.readd_old_references(new_func, func)
return new_func
def readd_old_references(self, new_func, old_func):
"""Adds back globals, function name and source reference."""
func = types.FunctionType(
code=self.add_src_ref(new_func.__code__, old_func.__code__),
globals=old_func.__globals__,
name=old_func.__name__,
argdefs=old_func.__defaults__,
closure=new_func.__closure__
)
func.__doc__ = old_func.__doc__
return func
def add_src_ref(self, new_code, old_code):
return types.CodeType(
new_code.co_argcount,
new_code.co_kwonlyargcount,
new_code.co_nlocals,
new_code.co_stacksize,
new_code.co_flags,
new_code.co_code,
new_code.co_consts,
new_code.co_names,
new_code.co_varnames,
old_code.co_filename, # reuse filename
new_code.co_name,
old_code.co_firstlineno, # reuse line number
new_code.co_lnotab,
new_code.co_freevars,
new_code.co_cellvars
)
def get_decorators(self, source, global_vars):
"""Creates a namespace for exec function creation in. Must remove
any reference to Inject decorator to prevent infinite recursion."""
namespace = {}
for match in self.decorator.finditer(source):
decorator = eval(match.group()[1:], global_vars)
basename = match.group(1)
if decorator is Inject:
namespace[basename] = DummyInject()
else:
namespace[basename] = global_vars[basename]
return namespace
def get_indent(self, lines):
"""Takes a set of lines used to create a function and returns the
outer indentation that the function is declared in and the inner
indentation of the body of the function."""
body_indent = None
function_body_start = self.get_function_body_start(lines)
for line in lines[function_body_start:]:
match = self.indent.match(line)
if match:
body_indent = match.group()
break
assert body_indent
match = self.indent.match(lines[0])
if not match:
declaration_indent = ""
else:
declaration_indent = match.group()
return declaration_indent, body_indent
if __name__ == "__main__":
a = 1
#Inject(b=10)
def f(c, d=1000):
"f uses injected variables"
return a + b + c + d
#Inject(var=None)
def g():
"""Purposefully generate exception to show stacktraces are still
meaningful."""
create_name_error # line number 164
print(f(100)) # prints 1111
assert f(100) == 1111
assert f.__doc__ == "f uses injected variables" # show doc is retained
try:
g()
except NameError:
raise
else:
assert False
# stack trace shows NameError on line 164
Which outputs the following:
1111
Traceback (most recent call last):
File "inject.py", line 171, in <module>
g()
File "inject.py", line 164, in g
create_name_error # line number 164
NameError: name 'create_name_error' is not defined
The whole thing is hideously ugly, but it works. It's also worth noting that if Inject is used for method, then any injected values are shared between all instances of the class.
You can do it using globals but I don't recommend this approach.
def enclose(name, value):
globals()[name] = value
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return decorator
#enclose('param1', 1)
def f():
global param1
param1 += 1
print(param1)
f()
I've just been reading an article that talks about implementing a parser in python:
http://effbot.org/zone/simple-top-down-parsing.htm
The general idea behind the code is described in this paper: http://mauke.hopto.org/stuff/papers/p41-pratt.pdf
Being fairly new to writing parsers in python so I'm trying to write something similar as a learning exercise. However when I attempted to try to code up something similar to what was found in the article I am getting an TypeError: unbound method TypeError. This is the first time I've encountered such an error and I've spent all day trying to figure this out but I haven't solved the issue. Here is a minimal code example (in it's entirety) that has this problem:
import re
class Symbol_base(object):
""" A base class for all symbols"""
id = None # node/token type name
value = None #used by literals
first = second = third = None #used by tree nodes
def nud(self):
""" A default implementation for nud """
raise SyntaxError("Syntax error (%r)." % self.id)
def led(self,left):
""" A default implementation for led """
raise SyntaxError("Unknown operator (%r)." % self.id)
def __repr__(self):
if self.id == "(name)" or self.id == "(literal)":
return "(%s %s)" % (self.id[1:-1], self.value)
out = [self.id, self.first, self.second, self.third]
out = map(str, filter(None,out))
return "(" + " ".join(out) + ")"
symbol_table = {}
def symbol(id, bindingpower=0):
""" If a given symbol is found in the symbol_table return it.
If the symblo cannot be found theni create the appropriate class
and add that to the symbol_table."""
try:
s = symbol_table[id]
except KeyError:
class s(Symbol_base):
pass
s.__name__ = "symbol:" + id #for debugging purposes
s.id = id
s.lbp = bindingpower
symbol_table[id] = s
else:
s.lbp = max(bindingpower,s.lbp)
return s
def infix(id, bp):
""" Helper function for defining the symbols for infix operations """
def infix_led(self, left):
self.first = left
self.second = expression(bp)
return self
symbol(id, bp).led = infix_led
#define all the symbols
infix("+", 10)
symbol("(literal)").nud = lambda self: self #literal values must return the symbol itself
symbol("(end)")
token_pat = re.compile("\s*(?:(\d+)|(.))")
def tokenize(program):
for number, operator in token_pat.findall(program):
if number:
symbol = symbol_table["(literal)"]
s = symbol()
s.value = number
yield s
else:
symbol = symbol_table.get(operator)
if not symbol:
raise SyntaxError("Unknown operator")
yield symbol
symbol = symbol_table["(end)"]
yield symbol()
def expression(rbp = 0):
global token
t = token
token = next()
left = t.nud()
while rbp < token.lbp:
t = token
token = next()
left = t.led(left)
return left
def parse(program):
global token, next
next = tokenize(program).next
token = next()
return expression()
def __main__():
print parse("1 + 2")
if __name__ == "__main__":
__main__()
When I try to run this with pypy:
Traceback (most recent call last):
File "app_main.py", line 72, in run_toplevel
File "parser_code_issue.py", line 93, in <module>
__main__()
File "parser_code_issue.py", line 90, in __main__
print parse("1 + 2")
File "parser_code_issue.py", line 87, in parse
return expression()
File "parser_code_issue.py", line 81, in expression
left = t.led(left)
TypeError: unbound method infix_led() must be called with symbol:+ instance as first argument (got symbol:(literal) instance instead)
I'm guessing this happens because I don't create an instance for the infix operations but I'm not really wanting to create an instance at that point. Is there some way I can change those methods without creating instances?
Any help explaining why this is happening and what I can do to fix the code is greatly appreciated!
Also is this behaviour going to change in python 3?
You forgot to create an instance of the symbol in your tokenize() function; when not a number, yield symbol(), not symbol:
else:
symbol = symbol_table.get(operator)
if not symbol:
raise SyntaxError("Unknown operator")
yield symbol()
With that one change your code prints:
(+ (literal 1) (literal 2))
You haven't bound new function to the instance of your object.
import types
obj = symbol(id, bp)
obj.led = types.MethodType(infix_led, obj)
See accepted answer to another SO question
What is the error below? Also, is there a better way to implement the following classes?
#!/usr/bin/python
class Datacenters:
def __init__(self,name,location,cpu,mem):
self.name=name
self.location=location
self.cpu=cpu
self.mem=mem
def getparam(self):
return self.name,self.location ,self.cpu,self.mem
def getname(self):
return self.name
class WS(Datacenters):
def __init__(self,name,location,cpu,mem,obj):
#datacentername = Datacenters.__init__(self) #To which data center it is associated
self.dcname =obj.name #To which data center it is associated
Datacenters.__init__(obj,name,location,cpu,mem)
def getparam(self,obj):
self.name,self.location ,self.cpu,self.mem = obj.getparam()
print self.dcname
#return self.name,self.location ,self.cpu,self.mem,obj.name
def getwsname(self):
return self.name
class Pcs(WS):
def __init__(self,name,location,cpu,mem,obj):
self.wsname = obj.getwsname() #To which WS it is associated
WS.__init__(obj,name,location,cpu,mem)
def getparam(self,obj):
print obj.getparam()
print self.wsname
a = Datacenters("dc1","Bl1",20,30)
print a.getparam()
b = WS("WS1","Bl1",21,31,a)
print b.getparam(a)
c = Pcs("PC1","Bl1",20,30,b)
#print c.getparam(b)
output:
Press ENTER or type command to continue
('dc1', 'Bl1', 20, 30)
dc1
None
Traceback (most recent call last):
File "class1.py", line 45, in <module>
c = Pcs("PC1","Bl1",20,30,b)
File "class1.py", line 34, in __init__
WS.__init__(obj,name,location,cpu,mem)
TypeError: __init__() takes exactly 6 arguments (5 given)
The error is that you pass in five arguments, but the __init__ needs six. Count them:
def __init__(self,name,location,cpu,mem,obj):
Six arguments. You call it like so:
WS.__init__(obj,name,location,cpu,mem)
Five arguments. The first one, self is missing. What you should ask yourself is why you don't have to pass in six arguments all the time.
And that is because self is passed in automatically when you call the method on an instance. However, in this case you don't call it on an instance, you call it directly on the class. There is of course no need to do so in this case, the correct syntax is:
WS(obj,name,location,cpu,mem)
As you indeed above note works further up.