# Efficient way to get selected vertices via python (without iterating over the entire mesh)

What's the best/fastest way to construct a list of selected vertices for a mesh object in python?

I want to avoid iterating over every vertex in the mesh if possible, since that seems like a really inefficient/wasteful way to go about it.

There's a nice attribute called select_history in the bmesh module that gives you a list of vertices in the order that they were selected...but of course, it will only give you verts that have been selected manually (not by box or circle select) after the bmesh was initialized.

I've been searching through the edit mesh api for an attribute that will give me an updated list of selected verts (so that I don't have to iterate over the whole mesh). Does such a thing not exist?

You have to iterate over the entire mesh since blender isn't storing this data in a separate list internally.

You can use the following line of code to get a list if selected vertices from a bpy.types.Mesh object ('mesh' in the code):

selected_verts = list(filter(lambda v: v.select, mesh.vertices))


or with list comprehension:

selected_verts = [v for v in mesh.vertices if v.select]


However, in most cases, brute force looping through all the vertices of the mesh should be fine.

• For this example I'd recommend using list comprehension, if you are going to use list(filter(...)), filter returns an iterator but if you only convert to a list immediately theres no reason to use it for simple checks like this. – ideasman42 Jun 28 '13 at 4:37
• I'm in Edit Mode with just one vertex selected, but v.select is true for all the vertices in the mesh :( – endavid Mar 30 '16 at 11:13
• It seems you have to switch back to Object mode so the selection gets "consolidated". I'll post an answer with the trick, just in case someone else gets stuck there. – endavid Mar 30 '16 at 11:30
• @endavid Exactly! I think this is a bug rather than a feature. – Sibbs Gambling Jun 23 '19 at 22:35
• This doesn't work for multi-object editing in 2.8. Any idea how to solve it? – ChameleonScales Jan 1 '20 at 20:07

Just building on Gwenn's great answer,

• If you are in Edit Mode, all the vertices may appear selected. Switch to Object Mode so the vertex selection gets updated.
• You can directly access the vertices from active_object.data, without looking for the mesh in bpy.data.meshes

Code:

mode = bpy.context.active_object.mode
# we need to switch from Edit mode to Object mode so the selection gets updated
bpy.ops.object.mode_set(mode='OBJECT')
selectedVerts = [v for v in bpy.context.active_object.data.vertices if v.select]
for v in selectedVerts:
print(v.co)
# back to whatever mode we were in
bpy.ops.object.mode_set(mode=mode)


Note that you can use the same logic to find selected edges,

selectedEdges = [e for e in bpy.context.active_object.data.edges if e.select]

• This should be the accepted answer. – Prasanth Feb 7 '19 at 15:09
import bpy
import numpy as np

ob = bpy.context.object
count = len(ob.data.vertices)
sel = np.zeros(count, dtype=np.bool)

ob.data.vertices.foreach_get('select', sel)

print(sel)

# super fast. No looping


The foreach_get and foreach_set methods are a fast way to get at certain collection properties in blender. They can be thousands of times faster than looping and hundreds of times faster than list comprehension. The code above returns a bool array. If you continue to work with numpy arrays in your code you can index subsets of your arrays with the bool array.

x = np.array([0, 0, 1, 0, 1], dtype=np.bool)
y = np.array([1, 2, 3, 4, 5])
print(y[x])

>>> (numpy array([3, 4]))

• the foreach_get and foreach_set methods are a fast way to get at certain collection properties in blender. They can be thousands of times faster than looping and hundreds of times faster than list comprehension. The code above returns a bool array. If you continue to work with numpy arrays in your code you can index subsets of your arrays with the bool array. x = np.array([0, 0, 1, 0, 1], dtype=np.bool)  y = np.array([1, 2, 3, 4, 5]) print(y[x]) >>> (numpy array([3, 4])) – Rich Colburn Sep 1 '17 at 20:34
• This is so fast! But what happens if the collection changes between the foreach_get and foreach_set operations? ie. bpy.context.scene.objects.foreach_get('select', s). Then two objects are deleted, or maybe added. bpy.context.scene.objects.foreach_set('select', s) will throw an array error. What might be a good way to handle situations like that? – andyV Jul 13 '18 at 21:03
• If you were keeping track of selected objects and objects might be deleted between the time you create the list and the time you retrieve the list, you have other issues. In this case you could set up a class or dictionary for each object and have a select attribute for your class. You could check to see if the object associated with your class/dictionary is still in the scene. In most cases, I don't have object or vert counts changing between foreach_get and foreach_set in the code. – Rich Colburn Jul 14 '18 at 21:46

problem concerning this subject, using blender v2.78

v.select value of v in bpy.context.active_object.data.vertices seems ALWAYS True:

• I go in 3D View, select a mesh, go in edit mode: all vertices appear selected
• I run below described script : all v.select values appear True
• I manually do a deselect all vertices (pressing A in 3D View, Edit mode)
• I run the script again : All v.select values remain True!

script does this:

for v in bpy.context.active_object.data.vertices:
print(str(v.select))


More completely:

I'm using a custom operator to perform this, which assures Edit mode as needed state and allows a shortkey setup to call the operator :

class stitch(bpy.types.Operator):
bl_idname = "my.printselectedstates"
bl_label = "printselectedstates"

@classmethod
def poll(cls, context):
ob = context.active_object
return ob is not None and ob.mode == 'EDIT'

def execute(self, context):
for v in context.active_object.data.vertices:
print(str(v.index) + ' ' + str(v.select))
return {'FINISHED'}

• – user42646 Aug 25 '17 at 4:28
• solution built in the code : inside that 'def execute' do a object/edit mode switch before the print loop. add these two lines bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.mode_set(mode='EDIT') – user42646 Aug 25 '17 at 4:28

Since Blender 2.8, multi-object editing makes the previously given answers insufficient, as they don't list the selected vertices of non active objects.

The following snippet solves this issue by creating a dictionary with the paths of the edited objects as keys and the indices of their respective selected vertices as key values :

import bpy
import bmesh

dic_v = {}
for o in bpy.context.objects_in_mode:
dic_v.update( {o : []} )
bm = bmesh.from_edit_mesh(o.data)
for v in bm.verts:
if v.select:
dic_v[o].append(v.index)

print(dic_v)


Related to the numpy approach which is super fast to get a count - the issue for me was - now that I have a count - how do I quickly get which vertex it is without loops:

    vert_count = len(obj.data.vertices)
sel = np.zeros(vert_count, dtype=np.bool)
obj.data.vertices.foreach_get('select', sel)
sel_count = np.count_nonzero(sel)

# get the actual vertices that are selected
nv = np.array(obj.data.vertices)
sel_vert_list = nv[sel]

• Is this an answer or a question? – Lukasz-40sth May 10 '20 at 10:59
• It is my solution that adds to the OP the code to get the actual list of selected vertices. – EMCS May 11 '20 at 13:27

In newer versions of Blender you can use (while in Edit Mode):

bpy.context.active_object.data.total_vert_sel
bpy.context.active_object.data.total_edge_sel
bpy.context.active_object.data.total_face_sel


Also count_selected_items() returns the number of selected items (vert, edge, face):

bpy.context.active_object.data.count_selected_items()


To programmatically switch to Edit Mode call: bpy.ops.object.mode_set(mode="EDIT”)

• Thanks @brockmann .. great tip/edit. – DolphinDream Feb 22 at 22:14

print index of selected vertex with order

import bpy
import bmesh

obj = bpy.context.object

data = obj.data
bm = bmesh.from_edit_mesh(data)

v = [s.index for s in bm.select_history if isinstance(s, bmesh.types.BMVert)]
print(v)

• As mentioned in question, select history only gives manually selected elements, not those selected by other means. – batFINGER Mar 9 '20 at 11:50