Script Randomly crashes Blender
Using Operator override

Here are a couple of examples.  This one finds all the mesh objects in the scene, sorts them alphabetically by name, then groups them by if they start with the first.

meshobs ['Cone', 'Cone.001', 'Cone.002', 'Cube', 'Cube.001', 'Cube.002', 'Cylinder', 'Cylinder.001', 'Cylinder.002']
Join ['Cone', 'Cone.001', 'Cone.002']
Join ['Cube', 'Cube.001', 'Cube.002']
Join ['Cylinder', 'Cylinder.001', 'Cylinder.002']


This is only a proof of concept and relies on "Cube" being in scene to match "Cube.nnn", add string partitioning etc as required.

The names of the matching objects are sent to the join method, which calls the operator with a context override dictionary, for active object and selected objects.

Note this Uses only the join operator which will greatly reduce the amount of operator calls from question code

Python performance with Blender operators

import bpy

context = bpy.context
scene = context.scene

def join(names):
    obs = list(filter(None, (scene.objects.get(n) for n in names)))
    print(obs)

    obs[0].select_set(True)
    bpy.ops.object.join(
            {"active_object" : obs[0],
             "object" : obs[0],
             "selected_objects" : obs,
             "selected_editable_objects" : obs
             })


meshobs = [o.name for o in scene.objects if o.type == 'MESH']
meshobs.sort()

while meshobs:
    n0 = meshobs[0]
    x = [n for n in meshobs if n.startswith(n0)]
    print("Joining", x)
    join(x)
    meshobs = meshobs[len(x):]


Alternatively using bmesh and  no operators

Can use bmesh to read in first mesh, then convert each other mesh into object space of first, and write back to first, then remove others.

This will keep only the materials of the first, etc etc, and may have to be altered accordingly.

import bpy
import bmesh


context = bpy.context
scene = context.scene

def bmesh_join(names):
    obs = list(filter(None, (scene.objects.get(n) for n in names)))
    print(obs)
    bm = bmesh.new()
    ob = obs.pop(0)
    mwi = ob.matrix_world.inverted()
    me = ob.data
    bm.from_mesh(me)
    for o in obs:
        o.data.transform(mwi @ o.matrix_world)
        bm.from_mesh(o.data)
        bpy.data.objects.remove(o)
    bm.to_mesh(me) 
    bm.free()
    me.update()   

meshobs = [o.name for o in scene.objects if o.type == 'MESH']
meshobs.sort()

while meshobs:
    n0 = meshobs[0] # fix for lpart on . : etc
    x = [n for n in meshobs if n.startswith(n0)]
    print("Join", x)
    bmesh_join(x)
    meshobs = meshobs[len(x):]