Render semi-transparent self-intersecting surface in cycles
two useful functions 

import bmesh
import bpy

def set_smooth(ob):
    mesh = ob.data
    smooth_states = [True] * len(mesh.polygons)
    mesh.polygons.foreach_set('use_smooth', smooth_states)
    mesh.update()

def remove_doubles(ob, d=0.00001):
    bm = bmesh.new()
    bm.from_mesh(ob.data)
    bmesh.ops.remove_doubles(bm, verts=bm.verts[:], dist=d)
    bm.to_mesh(ob.data)
    bm.free()


Those two functions aren't really meant for real-time stuff, but neither is bpy.ops stuff. They can however be integrated in a key-framed and rendered animation, no problem.

import bmesh
import bpy
import numpy as np

# set_smooth() and remove_doubles() are declared elsewhere

nth, nph = 101, 200
th  = np.linspace(-0.5*np.pi, 0.5*np.pi, nth)
phi = np.linspace(-np.pi, np.pi, nph)
y3 = np.sin(th) - 1.8*np.sin(th)**3
X = np.cos(phi)[None,:] * np.cos(th)[:,None]
Y = np.sin(phi)[None,:] * np.cos(th)[:,None]
Z = np.zeros_like(X)            + y3[:,None]

verts = [tuple(thing) for thing in zip(X.flatten(), Y.flatten(), Z.flatten())]
faces = []
for ith in range(nth-1):
    for iph in range(nph-1):
        v1 = nph*ith + iph
        v2 = v1 + 1
        v3 = v2 + nph
        v4 = v3 - 1
        faces.append((v1, v2, v3, v4))

me = bpy.data.meshes.new('wow')
me.from_pydata(verts,[],faces)
ob = bpy.data.objects.new('wow', me)
ob.location = (0.0, 0.0, 2.0) 
bpy.context.scene.objects.link(ob)
set_smooth(ob)
remove_doubles(ob)


setting up a cycles material via script is a little bit involved, and you might ask yourself, how much of this should I be doing via script or manually setting up the material and only adjust certain parameters via script at render time..

import bpy

scene = bpy.context.scene
scene.render.engine = 'CYCLES'

my_mat = bpy.data.materials.new('cool_mat')
my_mat.use_nodes = True
nodes = my_mat.node_tree.nodes

# material output and diffuse nodes are present already, get them by name
mat_output = nodes.get("Material Output")
diffuse = nodes.get("Diffuse BSDF") # new(type='ShaderNodeBsdfDiffuse')
# mix and transparent are added by the script
mix = nodes.new(type='ShaderNodeMixShader')
transp = nodes.new(type='ShaderNodeBsdfTransparent')

mat_output.location = 300, 0
mix.location = 0, 0
diffuse.location = -200, 50
transp.location = -200, -100

''' hook up shaders '''

# link mix_shader to material output
a = mix.outputs['Shader']
b = mat_output.inputs['Surface']
my_mat.node_tree.links.new(a, b)

# link diffuse to mix
a = diffuse.outputs['BSDF']
b = mix.inputs[1]  # 'Shader' is used twice, so you access via index.
my_mat.node_tree.links.new(a, b)

# link transparent to mix
a = transp.outputs['BSDF']
b = mix.inputs[2]
my_mat.node_tree.links.new(a, b)

''' small shader adjustments and mixing '''

mix.inputs[0].default_value = 0.8  # factor
diffuse.inputs['Color'].default_value = [0.8, 0.0, 0.191713, 1.0]
transp.inputs['Color'].default_value = [1.0, 0.152801, 0.582658, 1.0]


which combined, gives something like this:



I suspect a node setup like this might be a little more interesting: (note: I have ambient occlusion on!)



The idea is to make the color or factor a function of the cross product between surface normal and the up vector (0,0,z). Using the nodes allows you to embrace vector mathematics for the adjustment of the shader at local points.

or taking the viewing angle into account too:



And notice there is a backfacing output on the Geometry node, worth exploring