Here is a handy mobius strip. Mathematically it has one surface, but in Blender it's just a twisted flat mesh stitched together carefully. I added some icospheres as breadcrumbs to guide the eye. I am going to try to add some patterning to the "surface", either a color gradient or a UV-map in the future.
But, right now, I am wondering if using backfacing in nodes as @BenSimonds shows in the second answer here is going to be robust, or if I should just get used to making my 2D surfaces as really thin 3D double-surfaces (e.g. a flattened tube in this case) and avoid any potential future problems. There may be some experience out there about things that can go wrong here that I can't find with google.
Note: surfaces will be pretty complicated, and making them thin 3D shapes will be tricky - this is a simple example for discussion.
import numpy as np
import bpy
R, W = 2.0, 1.0
nth, nw = 120, 9
th = np.linspace(-0.5*np.pi, 1.5*np.pi, nth+1)[:-1][:,None]
w = np.linspace(-0.5*W, 0.5*W, nw)[None,:]
phi = 0.5*np.pi*np.cos(th.clip(0.0, np.pi))
x = (R + w*np.cos(phi)) * np.cos(th)
y = (R + w*np.cos(phi)) * np.sin(th)
z = (0 + w*np.sin(phi))
nx = np.cos(th)*np.sin(phi)
ny = np.sin(th)*np.sin(phi)
nz = -np.cos(phi)
verts0 = [tuple(thing) for thing in zip(x.flatten(), y.flatten(), z.flatten())]
faces0 = []
for ith in range(nth-1):
for iw in range(nw-1):
v1 = nw*ith + iw
v2 = v1 + 1
v3 = v2 + nw
v4 = v3 - 1
faces0.append((v1, v2, v3, v4))
for iw in range(nw-1):
v1 = nw*(ith-2) + iw
v2 = v1 + 1
v3 = nw*0 + nw-2 - iw # last one is sideways 'cause it's a mobius!
v4 = v3 + 1
faces0.append((v1, v2, v3, v4))
blueish = bpy.data.materials.new("BLUEISH")
blueish.diffuse_color = (0.1, 0.3, 1.0)
blueish.specular_color = (200, 1.0, 100) # CHANGE THIS!
mobi_mesh_0 = bpy.data.meshes.new('mobi0')
mobi_obj_0 = bpy.data.objects.new('mobi0', mobi_mesh_0)
bpy.context.scene.objects.link(mobi_obj_0)
mobi_mesh_0.from_pydata(verts0, [], faces0)
mobi_obj_0.active_material = blueish
bpy.data.objects['mobi0'].select = False
bpy.data.objects['mobi0'].select = True
bpy.ops.object.shade_smooth()
d = 0.11
for i in range(0, nth, 5):
xp = x[i,4] + d*nx[i]
yp = y[i,4] + d*ny[i]
zp = z[i,4] + d*nz[i]
q = bpy.ops.mesh.primitive_ico_sphere_add(size=0.1, location=(xp, yp, zp))