I'm using PyCharm and I'm trying to do some refactoring but don't see how I'm able to do this in a fast and reliable way.
I have a method that does too many things and I want to extract a part into another method. The extracted method should not be called in the method it was extracted though, but rather in the calling method.
Current state
class User():
def doStuff(self):
calculateA()
calculateB()
calculateC()
def callerA():
# do other things before
obj.doStuff()
def callerAA:
# do other things before
obj.doStuff()
#... and many more methods calling doStuff method
Wanted
class User():
def doStuff(self):
calculateA()
def doOtherStuff(self):
calculateB()
calculateC()
def callerA():
obj.doStuff()
obj.doOtherStuff()
def callerAA:
obj.doStuff()
obj.doOtherStuff()
#... and many more methods calling doStuff method and doOtherStuff
# Now I'll be able to create this new method only doing a subset of the original method
def aNewMethod:
obj.doStuff()
Is this possible to do with PyCharm? Been playing around with the refactoring without any luck. Extracting into a method is the easy part I suppose but the method call will end up in the wrong place. If it's possible in Intellij I have a license for that as well so I can just switch.
there is no such option. You are welcome to submit a feature request at https://youtrack.jetbrains.com/issues/PY
Information on how to use YouTrack: https://intellij-support.jetbrains.com/hc/en-us/articles/207241135-How-to-follow-YouTrack-issues-and-receive-notifications
Step1: Extract two new methods into your User class
This:
class User():
def doStuff(self):
calculateA()
calculateB()
calculateC()
Becomes:
class User():
def doStuff(self):
newMethodA()
newMethodB()
def newMethodA(self):
calculateA()
def newMethodB(self):
calculateB()
calculateC()
Step 2: Inline doStuff method
class User():
def newMethodA(self):
calculateA()
def newMethodB(self):
calculateB()
calculateC()
def callerA():
newMethodA()
newMethodB()
def callerAA():
newMethodA()
newMethodB()
Step 3: Rename methods
Related
Please have a look at the below code
class MyStudent:
def __init__(self, student_id, student_name):
self._student_id = student_id
self._student_name = student_name
def get_student_id(self):
return self._student_id
def get_student_name(self):
return self._student_name
student1 = MyStudent(student_id=123, student_name="ABC")
student1.get_student_id()
student1.get_student_name()
I would like to run some code like adding the student to the DB whenever student1.get_student_id() or student1.get_student_name() is invoked (or when get() is accessed. Please correct me if i used the wrong descriptor). And I have to do this via Decorators only for multiple classes like below
#save_student_to_db
class MyStudent:......
How can this be achieved using Decorators? I need to use a single decorator for multiple classes which can have any method. Whenever any method (apart for the ones starting with _ or __) of any class is called the decorator should save the data to DB
If all classes implement the same methods, say get_student_id and get_student_name, and has the same attributes, say _student_id and _student_name, then you can do a class decorator like so:
from functools import wraps
from somewhere import Database
#wraps(fn)
def _method_decorator(fn):
def getter_wrapper(self):
db = Database()
db.save(self._student_id, self._student_name)
return fn(self)
return getter_wrapper
def save_student_to_db(cls):
cls.get_student_id = _method_decorator(cls.get_student_id)
cls.get_student_name = _method_decorator(cls.get_student_name)
return cls
About the database, you can instantiate it every time its needed like I proposed above or have a dependency injection framework do it for you. I've been using injectable for a while and it is quite simple and yet powerful, there is serum too.
I have a python file which can be resumed as follow :
from external_libs import save
class FakeClass1(MotherFakeClass1):
#property
def field(self):
if self.settings['save_parameter_booelan']:#settings come from the mother class but irelevant
import FakeClass2.save as save
# I want to override the save method by the one defined in the FakeClass2
return BehaviorModifierField(super(FakClass1, self).field) #The behavior Modifier decorate the new field but it's irelevant of what it does.
return super(FakClass1, self).field
def fakeMethod(self, boolean_val):
save('blabla')
class FakeClass2:
#staticmethod
def save(test):
#irrelevant core of the method
The idea is here but I struggle to find the right to do this.
I think I could do it more properly if I could move the FakeClass2 in another file but I don't want to.
Do you have a better idea ?
A staticmethod is not the right choice, as its invoked via Class.method. Your save is not used like that. And even if it were, it's on FakeClass, not FakeClass2.
If you want to invoke a different save depending on the class, just add a save METHOD (not function!) and use the function of choice. E.g.
from library import standard_save
class A:
def work(self):
self.save()
def save(self):
standard_save()
class B(A):
def save(self):
do_something_else()
Don't pass a boolean; pass the function to be used.
import external_libs
class FakeClass1:
def __init__(self, save_function=external_libs.save):
self.save = save_function
# Does this method do anything other than change the save function
# to use? If not, it can be eliminated.
def field(self):
# Use self.save as the function to save things
def fakeMethod(self, boolean_val):
self.save('blabla')
class FakeClass2:
#staticmethod
def save(test):
#irrelevant core of the method
instance1 = FakeClass1(FakeClass2.save)
instance2 = FakeClass1() # Default of external_libs.save
I think you are overcomplicating things.
Having a boolean value determining the behaviour of the save method means that the save method behaves differently depending on the instance of the class, according to the boolean value of the instance.
If this is what you want, this is the simplest way I can think.
class FakeClass1(MotherFakeClass1):
def __init__(self):
#your __init__ here
def save(self):
if self.settings['save_parameter_booelan']:
FakeClass2.save()
#or whatwever method you want to use in this case,
#even if is not this class method or another class method
else:
raise NotImplementedError
#or whatever code should be executed by save
#when save_parameter_boolean is false
I have following example code:
class AuxiliaryClass:
#staticmethod
def high_cost_method():
"Do something"
class MyTestedClass:
def do_something(self):
something = AuxiliaryClass.high_cost_method()
"do something else"
I want to test MyTestedClass. For this purpose I've created AuxiliaryClassStub class to override high_cost_method(). I want my test to execute do_something() from MyTestedClass, but do_something() should use stub instead of real class.
How can I do that?
My real auxiliary class is quite big, it has a lot of methods and I will use it in many tests, so I don't want to patch single methods. I need to replace whole class during tests.
Note, that high_cost_method() is static, so mocking __init__() or __new__() will not help in this case.
Does it work if you use self.__class__.high_cost_method inside do_something? This way you avoid the direct reference to the class name, which should enable subclassing and overriding the staticmethod with the one from AuxiliaryClass.
class MyTestedClass:
def do_something(self):
something = self.__class__.high_cost_method()
something()
#staticmethod
def high_cost_method():
print("high cost MyTestedClass")
class AuxiliaryClass(MyTestedClass):
#staticmethod
def high_cost_method():
print("high cost AuxiliaryClass")
Then you get
test = AuxiliaryClass()
test.high_cost_method()
high cost AuxiliaryClass
and otherwise
test = MyTestedClass()
test.high_cost_method()
high cost MyTestedClass
Goal: Make it possible to decorate class methods. When a class method gets decorated, it gets stored in a dictionary so that other class methods can reference it by a string name.
Motivation: I want to implement the equivalent of ASP.Net's WebMethods. I am building this on top of google app engine, but that does not affect the point of difficulty that I am having.
How it Would look if it worked:
class UsefulClass(WebmethodBaseClass):
def someMethod(self, blah):
print(blah)
#webmethod
def webby(self, blah):
print(blah)
# the implementation of this class could be completely different, it does not matter
# the only important thing is having access to the web methods defined in sub classes
class WebmethodBaseClass():
def post(self, methodName):
webmethods[methodName]("kapow")
...
a = UsefulClass()
a.post("someMethod") # should error
a.post("webby") # prints "kapow"
There could be other ways to go about this. I am very open to suggestions
This is unnecessary. Just use getattr:
class WebmethodBaseClass():
def post(self, methodName):
getattr(self, methodName)("kapow")
The only caveat is that you have to make sure that only methods intended for use as webmethods can be used thus. The simplest solution, IMO, is to adopt the convention that non-webmethods start with an underscore and have the post method refuse to service such names.
If you really want to use decorators, try this:
def webmethod(f):
f.is_webmethod = True
return f
and get post to check for the existence of the is_webmethod attribute before calling the method.
This would seem to be the simplest approach to meet your specs as stated:
webmethods = {}
def webmethod(f):
webmethods[f.__name__] = f
return f
and, in WebmethodBaseClass,
def post(self, methodName):
webmethods[methodName](self, "kapow")
I suspect you want something different (e.g., separate namespaces for different subclasses vs a single global webmethods dictionary...?), but it's hard to guess without more info exactly how your desires differ from your specs -- so maybe you can tell us how this simplistic approach fails to achieve some of your desiderata, so it can be enriched according to what you actually want.
class UsefulClass(WebmethodBaseClass):
def someMethod(self, blah):
print(blah)
#webmethod
def webby(self, blah):
print(blah)
class WebmethodBaseClass():
def post(self, methodName):
method = getattr(self, methodName)
if method.webmethod:
method("kapow")
...
def webmethod(f):
f.webmethod = True
return f
a = UsefulClass()
a.post("someMethod") # should error
a.post("webby") # prints "kapow"
I have decorator #login_testuser applied to method test_1():
class TestCase(object):
#login_testuser
def test_1(self):
print "test_1()"
Is there a way I can apply #login_testuser on every method of the class prefixed with "test_"?
In other words, the decorator would apply to test_1(), test_2() methods below, but not on setUp().
class TestCase(object):
def setUp(self):
pass
def test_1(self):
print "test_1()"
def test_2(self):
print "test_2()"
In Python 2.6, a class decorator is definitely the way to go. e.g., here's a pretty general one for these kind of tasks:
import inspect
def decallmethods(decorator, prefix='test_'):
def dectheclass(cls):
for name, m in inspect.getmembers(cls, inspect.isfunction):
if name.startswith(prefix):
setattr(cls, name, decorator(m))
return cls
return dectheclass
#decallmethods(login_testuser)
class TestCase(object):
def setUp(self):
pass
def test_1(self):
print("test_1()")
def test_2(self):
print("test_2()")
will get you what you desire. In Python 2.5 or worse, the #decallmethods syntax doesn't work for class decoration, but with otherwise exactly the same code you can replace it with the following statement right after the end of the class TestCase statement:
TestCase = decallmethods(login_testuser)(TestCase)
Sure. Iterate all attributes of the class. Check each one for being a method and if the name starts with "test_". Then replace it with the function returned from your decorator
Something like:
from inspect import ismethod, getmembers
for name, obj in getmembers(TestCase, ismethod):
if name.startswith("test_"):
setattr(TestCase, name, login_testuser(obj))
Are you sure you wouldn't be better off by putting login_testuser's code into setUp instead? That's what setUp is for: it's run before every test method.
Yes, you can loop over the class's dir/__dict__ or have a metaclass that does so, identifying if the attributes start with "test". However, this will create less straightforward, explicit code than writing the decorator explicitly.