# Why avoid bpy.ops?

Simple and maybe stupid question:

I was reading this lot of time, that

"you should avoid using bpy.ops, or "instead of bpy.ops, use this and that"...

So why is that? Is that buggy, or what?

• All I can think of is that it's context sensitive (they require a certain mode or certain selection). But I'll let someone more experienced answer properly. – Greg Zaal Sep 12 '13 at 14:55
• Operators often cause implicit scene updates, which can lead to quadratic runtime increase if you use them in a loop (e.g. object duplication). It may take hours to duplicate, 'cause every object in the scene is updated after each added object - and the number of objects to process increases on every iteration. An equivalent "low level" code can duplicate an object thousands of times in less than a minute and call Scene.update() once at the very end. – CoDEmanX Feb 15 '14 at 1:07
• Watch the example: Basically it is not practical under some sircumstances. youtube.com/watch?v=KNa5kJd2Epo&t=43m50s – Max Villafranca Jan 13 '18 at 9:02

Lots of bpy.ops operators depend on the context (selected objects, active shape key index, etc.), therefore I think some are more suitable to be used interactively than in a script.

I'd use bpy.ops judiciously rather than avoiding it wholesale. For example, I find creating constraint through a posebone's constraints.new easier to read, and independent from context, compared to changing context then calling pose.constraint_add_with_targets. But bpy.ops can be indispensable for some tasks like vertex group manipulation and file operation even though it means temporarily messing with context.

For me, it really depends on what you're trying to do. Before resorting to bpy.ops.*, see if the task can be accomplished in any other way. Using bpy.ops operators may not always be best, but careful application can still result in readable code and smooth user interaction.

• This answer shows a comparison on how performance gets affects if you use bpy.ops – Amir Oct 25 '18 at 20:15

I'll just quote ideasman42:

When possible I would avoid using bpy.ops, These wrap tools in a way that relies on the context and dont have a good way to pass args and return results to scripts. If there is an alternative (as with bmesh), it's better integrated into python, and you don't have to worry about active object, modes, visible layers etc.

Avoiding bpy.ops may boost performance of your scripts when manipulating lots of objects. See: Python performance with Blender operators

Apart from what's been said already, operators also create undo steps. This means that every time you execute an operator, the entire blend file is saved in memory. This is one of the reasons calling an operator is much slower than the corresponding alternative function calls.

This is also why it's a good idea to, when writing an operator, place the major part of the code into regular Python functions, and call those from the operator code. This allows others to import your module and call those functions as well, from their own code.

• I'm not trying to use operators but would like to know whether the undo steps issue get resolved, even partially, if you set bpy.context.user_preferences.edit.undo_steps to 0 and bpy.context.user_preferences.edit.use_global_undo to False? – Amir Sep 23 '18 at 22:54
• One thing I usually get confused about is how I can totally avoid using operators? For instance, how can I add a cube to the scene without using bpy.ops.mesh.primitive_cube_add()? Should I manually put vertices in the scene and then make polygons? My point is it looks like there is no official guideline on how people can avoid using bpy.ops – Amir Sep 23 '18 at 22:58
• Sometimes it's not possible to avoid operators. Operators also won't create undo steps when you call them from another operator. For mesh creation it depends on your need. Calling the operator is much easier and not all that slow, but when you have to create 10000 cubes of various sizes and positions, it'll probably be better to 'manually' generate the geometry. – dr. Sybren Sep 24 '18 at 10:10