How can I create easily editable stylized hairs and fur?
Note: at the end of this post I am giving you a script that does all of that automatically

Stylized hair creation

The curves allow to create strands of hairs that you can quickly place and shape. You will need three things, the hair strand direction, a taper curve for the width and a taper for the shape to extrude.

Hair strand

Add a Curve > Bezier, in edit mode move it of 1 unit to the right so the root of the hair strand is on the origin. In the side view from the right (numpad 3) rotate the hairs in -90 degrees in edit mode.

Scale the curve in object mode to 0.15, then apply the scale.

Rotate the tip at 90 degrees so it faces downward and move it down.

For the resolution Preview U to 64. You can name it "hair_strand" so you can select them more easily in the outliner.

Taper

The taper determine how wide the hair strand is. The space between the origin and the curve allow to adjust it. Add a curve, from the top view rotate the base at 35 degrees and move it up by 0.04 units. Rotate the tip by 25 degrees

in top view rotate the tip at 15 degrees. You can scale it down to 0.15, do not apply the scale this time. Name it "hair_taper".

Bevel

The bevel is the shape that is extruded along the curve. To create it add a Curve > Circle change the Handle Type to Vector with the V shortcut. Scale it to 0.1 in edit mode then in Y by 0.35. Then set the vector type to automatic.
Name it "hair_bevel".

Here is the result (base mesh: http://www.dota2.com/workshop/requirements/lina ):



Place the hair strand and use Ctrl + T to tilt it.

The script (Blender 2.8+):

import bpy

# Hair Strand

bpy.ops.curve.primitive_bezier_curve_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0))


bpy.ops.object.origin_set(type='ORIGIN_CURSOR')
bpy.ops.object.origin_set(type='GEOMETRY_ORIGIN')

bpy.ops.object.mode_set(mode = 'EDIT')

bpy.ops.transform.translate(value=(1, 0, 0), constraint_axis=(True, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.transform.rotate(value=-1.5708, axis=(-1, -2.22045e-16, -4.93038e-32), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)

bpy.ops.object.mode_set(mode = 'OBJECT')

bpy.ops.transform.resize(value=(0.15, 0.15, 0.15), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)

bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.curve.de_select_first()
bpy.ops.transform.rotate(value=1.5708, axis=(-0, 1, 1.34359e-07), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)

bpy.ops.transform.translate(value=(-0.25, 0, -0.2), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.object.mode_set(mode = 'OBJECT')

bpy.context.object.data.resolution_u = 64

bpy.context.object.name = "HairStrand"

# Hair Taper

bpy.ops.curve.primitive_bezier_curve_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0))
bpy.ops.transform.resize(value=(0.15, 0.15, 0.15), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.transform.translate(value=(0, -0.8, 0), constraint_axis=(False, True, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)

bpy.ops.object.mode_set(mode = 'EDIT')

bpy.ops.curve.de_select_last()
bpy.ops.transform.rotate(value=0.610865, axis=(-0, -0, -1), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)

bpy.ops.transform.translate(value=(0, 0.04, 0), constraint_axis=(False, True, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.curve.de_select_last()


bpy.ops.curve.de_select_first()
bpy.ops.transform.rotate(value=0.436332, axis=(-0, -0, -1), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)


bpy.ops.object.mode_set(mode = 'OBJECT')
bpy.context.object.name = "HairTaper"

# Hair Bevel

bpy.ops.curve.primitive_bezier_circle_add(radius=1, view_align=False, enter_editmode=False, location=(0, 0, 0))

bpy.ops.object.mode_set(mode = 'EDIT')
bpy.ops.curve.handle_type_set(type='VECTOR')
bpy.ops.transform.resize(value=(0.1, 0.1, 0.1), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.transform.resize(value=(1, 0.35, 1), constraint_axis=(False, True, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)
bpy.ops.curve.handle_type_set(type='AUTOMATIC')
bpy.ops.object.mode_set(mode = 'OBJECT')

bpy.ops.transform.translate(value=(0, -1, 0), constraint_axis=(False, True, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1)

bpy.context.object.name = "HairBevel"

# Import curves

bpy.ops.object.select_all(action = 'DESELECT')
bpy.ops.object.select_pattern(pattern="HairStrand")
bpy.context.view_layer.objects.active  = bpy.data.objects["HairStrand"]

bpy.context.object.data.taper_object = bpy.data.objects["HairTaper"]
bpy.context.object.data.bevel_object = bpy.data.objects["HairBevel"]

bpy.ops.object.mode_set(mode = 'EDIT')