I am trying to edit python scripts in first time and it drives me crazy :( .
I have some directory with *.py files, that I added to PyCharm as Interpreter Paths, for correct auto completion.
So, I have some class
class Sim():
def __init__(self, *args, **kwargs):
self._sim_info = None
I am Java and C++ programmer and I am used to class type variables.
I know, that in scripts this variable will have value with type SimInfo.
But, when indexer of the PyCharm indexed that *.py files, he knows, that Sim.sim_info var has value None. But how can specify, that in code
s1=Sim()
i=s1.sim_info
variable i has type of class SimInfo?
May be I should use something like "editor hits", to force auto completion for i.is_ghost?
For example, code
from simulation.sims.sim import Sim
from simulation.sims.sim_info import SimInfo
from simulation.sims.pregnancy.pregnancy_tracker import PregnancyOffspringData
s1=Sim()
i=s1.sim_info
i.is_ghos
where i.is_ghos must be auto completed to i.is_ghost().
How to specify variable types in this case (may be via something like editor hints)?
Thank you very much!
Python 3.6:
class Sim():
def __init__(self, *args, **kwargs):
self._sim_info: SimInfo = None
Other python:
class Sim():
def __init__(self, *args, **kwargs):
self._sim_info = None # type: SimInfo
It called "type hints"
You can use type hinting with pycharm using the #type docstring.
def __init__(self, *args, **kwargs):
# n.b., in your usage code you use `.sim_info`
# but in your constructor you used `._sim_info`
# since I didn’t see a prop get function, I assumed
# that you meant `.sim_info`
self.sim_info = None
"""#type: SimInfo"""
or
def __init__(self, *args, **kwargs):
self.sim_info = None
""":type SimInfo"""
You can also specify the full path to the class including the module if just the class name did not work. You can also use PEP484 in PyCharm to specify the type of member variables:
def __init__(self):
self.sim_info = None # type: SimInfo
Related
I'm using Python 3.7.6 with PyCharm. I want my code to work as a nice internal API with code completion appearing for objects so I want to use typing.
I'm trying to find a good pattern for when:
There are two groups of classes with each group having it's own inheritance tree
and objects from one group are composed of lists of objects from other group
(example below)
I've found a way below but it feels like a hack. What's the right (or better) way to do this?
from typing import List, Type
class Leg:
def step(self):
print("step")
class DuckLeg(Leg):
def paddle(self):
print("splosh")
class Biped:
def __init__(self, leg_type: Type[Leg]):
self.legs: List[leg_type] = [leg_type(), leg_type()]
def walk(self):
for leg in self.legs:
leg.step()
class Duck(Biped):
def __init__(self):
super().__init__(leg_type=DuckLeg)
self.legs: List[DuckLeg] = self.legs # A hack?
my_duck = Duck()
my_duck.walk() # code-completion appears for .walk()
my_duck.legs[0].paddle() # code-completion appears for .paddle()
Edit 1: This question is not about where to put the type annotations but how to ensure code-completion works in this context. If the following line is commented-out...
self.legs: List[DuckLeg] = self.legs
...the code will still run because of duck-typing but code-completion will not appear for .paddle() and when manually entered PyCharm code inspection will report: Unresolved attribute reference 'paddle' for class 'Leg'.
Not sure if this actually solves your problem but I think it's cleaner than what you propose:
from typing import List, Type
class Leg:
def step(self):
print("step")
class DuckLeg(Leg):
def paddle(self):
print("splosh")
class Biped:
LegType = Leg
def __init__(self):
# self.LegType always gives the correct leg type for the instance
self.legs: List[self.LegType] = [self.LegType(), self.LegType()]
def walk(self):
for leg in self.legs:
leg.step()
class Duck(Biped):
LegType = DuckLeg
def __init__(self):
super().__init__()
I am currently using argparse in a class much like this:
class MyClass:
P_OPT = "my-opt"
"""This is an option to my script."""
P_OPT_HELP = "This is an option to my script"
"""Description for argparse"""
def __init__(self, **kwargs):
# do stuff here
pass
#classmethod
def parse_arguments(cls):
parser = argparse.ArgumentParser()
parser.add_argument('--' + cls.P_OPT, help=cls.P_OPT_HELP)
def main():
MyClass(**MyClass.parse_arguments())
Works fine, but I have a lot of options and arguments, there is redundancy between the P_OPT docstring and the content of P_OPT_HELP. Also I have to make a docstring for P_OPT_HELP too otherwise my project linter will remain unhappy. PEP 224 decided that there would be no built-in way to get to class and instance attributes docstring at runtime.
Is there a module or a clever python trick to do this ?
What I am considering so far is just using dict, it does not prevent the redundancy but does not pollute my class namespace so much:
P_OPT = {"name": "my-opt", "help": "This is an option to my script"}
"""This is an option to my script"""
And then:
parser.add_argument('-' + P_OPT["name"], help=P_OPT["help"])
I created the following class:
import loader
import pandas
class SavTool(pd.DataFrame):
def __init__(self, path):
pd.DataFrame.__init__(self, data=loader.Loader(path).data)
#property
def path(self):
return path
#property
def meta_dict(self):
return loader.Loader(path).dict
If the class is instantiated the instance becomes a pandas DataFrame which I wanted to extend by other attributes like the path to the file and a dictionary containing meta information (called 'meta_dict').
What I want is the following: the dictionary 'meta_dict' shall be mutable. Namely, the following should work:
df = SavTool("somepath")
df.meta_dict["new_key"] = "new_value"
print df.meta_dict["new_key"]
But what happens is that every time I use the syntax 'df.meta_dict' the method 'meta_dict' is called and the original 'meta_dict' from loader.Loader is returned such that 'df.meta_dict' cannot be changed. Therefore, the syntax leads to "KeyError: 'new_key'". 'meta_dict' shall be called only once and then never again if it is used/called a second/third... time. The second/third... time 'meta_dict' should just be an attribute, in this case a dictionary.
How can I fix this? Maybe the whole design of the class is bad and should be changed (I'm new to using classes)? Thanks for your answers!
When you call loader.Loader you'll create a new instance of the dictionary each time. The #property doesn't cache anything for you, just provides a convenience for wrapping complicated getters for a clean interface for the caller.
Something like this should work. I also updated the path variable so it's bound correctly on the class and returned in the path property correctly.
import loader
import pandas
class SavTool(pd.DataFrame):
def __init__(self, path):
pd.DataFrame.__init__(self, data=loader.Loader(path).data)
self._path = path
self._meta_dict = loader.Loader(path).dict
#property
def path(self):
return self._path
#property
def meta_dict(self):
return self._meta_dict
def update_meta_dict(self, **kwargs):
self._meta_dict.update(kwargs)
Another way to just cache the variable is by using hasattr:
#property
def meta_dict(self):
if not hasattr(self, "_meta_dict"):
self._meta_dict = loader.Loader(path).dict
return self._meta_dict
I'm trying to create a debug console for the main console I'm writing using Cmd module.
The debug console should have all the main console attributes and ontop some more extentions used for debug and advanced users.
The best answer to fir my needs was the second answer of the following post:
object inheritance and nested cmd
My implementation currently looks like this:
class MainConsole(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
def do_something(self, line):
print "do something!"
return
def do_something2(self, line):
print "do something2!"
return
class SubConsole1(cmd.Cmd):
def __init__(self, maincon):
cmd.Cmd.__init__(self)
self.maincon = maincon
self.register_main_console_methods()
def register_main_console_methods(self):
main_names = self.maincon.get_names()
for name in main_names:
if (name[:3] == 'do_') or (name[:5] == 'help_') or (name[:9] == 'complete_'):
self.__dict__[name] = getattr(self.maincon, name)
Observation:
When I hit "help", I indeed see all the upper console methods and I'm able to invoke them.
Problem:
The autocompletion of the actual commands is not available.
The expected behaviour of the shell when hitting "some" and tab would be to autocomplete it to "something". This doesn't happen.
When I tried to debug the issue, I found out that self.get_names() method which is used by the self.completenames() function returns the list of methods before the registration.
So what's actually happening is that the newly added methods are "removed" from the nested console, although I can invoke them.
I'd love some insights on that.
Thanks!
You can solve your problem by extending get_names method
import cmd
class MainConsole(cmd.Cmd):
def __init__(self,console_id):
cmd.Cmd.__init__(self)
self.console_id = console_id
def do_something(self, line):
print "do something!",self.console_id
return
def do_something2(self, line):
print "do something2!",self.console_id
return
class SubConsole1(cmd.Cmd):
def __init__(self, maincon):
cmd.Cmd.__init__(self)
self.maincon = maincon
self.register_main_console_methods()
def do_super_commands(self,line):
print "do supercommand",self.maincon
def register_main_console_methods(self):
main_names = dir(self.maincon)
for name in main_names:
for prefix in 'do_','help_','complete_', :
if name.startswith(prefix) and name not in dir(self):
self.__dict__[name] = getattr(self.maincon, name)
def get_names(self):
result = cmd.Cmd.get_names(self)
result+=self.maincon.get_names()
return result
SubConsole1(MainConsole("mainconsole")).cmdloop()
it is not guaranteed to work on subsequence version of python as it is undocumented behavior of python 2.7
EDIT: replacing the subclassing method by mainconsole as a member as required in comment
EDIT 2: don't replace the existing methods in SubConsole to keep method as do_help
The scenerio is I'm using an arg parser to get a command line argument auth_application.
auth_application command can have many values, for example:
cheese
eggs
noodles
pizza
These values are related to a programmable class.
I'd like a way to name the class, possible using a decorator.
So I can say
if auth_application is Cheese.__name__:
return Cheese()
Currently I maintain a tuple of auth_application names and have to expose that to my arg parser class as well as import the classes I need.
Anyways to make this better? Is there a decorator for classes to name them?
I'm looking for a python 2.7 solution, but a python 3 solution might be useful to know.
Easy peasy.
class command(object):
map = {}
def __init__(self, commandname):
self.name = commandname
def __call__(self, cls):
command.map[self.name] = cls
return cls
class NullCommand(object):
pass
#command('cheese')
class Cheese(object):
pass
#command('eggs')
class Eggs(object):
pass
def func(auth_application):
return command.map.get(auth_application, command.NullCommand)()
You can just keep a sinlge list of all of your "allowed classes" and iterate over that to find the class being referred to from the command line.
allow_classes = [Cheese,Eggs,Noodles,Pizza]
for cls in allow_classes:
if auth_application.lower() is cls.__name__.lower():
return cls()
Absolutely you can! You need to understand class attributes.
class NamedClass(object):
name = "Default"
class Cheese(NamedClass):
name = "Cheese"
print(Cheese.name)
> Cheese
You can use the standard Inspect Library to get the real class names, without having to augment your classes with any extra data - and this works for any class, in any module - even if you don't have the source code.
For instance - to list all the classes defined in mymodule :
import mymodule
import inspect
for name, obj in inspect.getmembers(mymodule, inspect.isclass):
print name
the obj variable is a real class object - which you can use to declare an instance, access class methods etc.
To get the definition of a class by it's name string - you can write a simple search function :
import mymodule
import inspect
def find_class(name):
"""Find a named class in mymodule"""
for this_name, _cls_ in inspect.getmembers(mymodule, inspect.isclass):
if this_name = name:
return _cls_
return None
....
# Create an instance of the class named in auth_application
find_class(auth_application)(args, kwargs)
NB: Code snippets not tested