A deeper discussion can be found on various Renderman help pages (e.g. https://renderman.pixar.com/view/how-to-primitive-variables)
A similar concept exists in Maya DG nodes called TripleSwitch.
In ShaderFX, we can do this using the Primitive Variable node for float values, or the TextureMap node for textures (string attributes).
You start by adding a custom attribute onto your Maya Shape node. Note that it is important the attribute ends up on the Shape and not on the transform node or any other node. So make sure you select the shape node before adding the custom attribute.
The name you use for the custom attribute, is the name we are also going to use on the "Primitive Variable" node inside ShaderFX.
(You have to switch ShaderfX into "Advanced Mode" to find the Primitive Variable node)
Now that we have the custom attribute on the shape node and we have added a shaderFX shader to the object and changed its graph to contain the Primitive Variable node, we can change the value of the custom attribute for each shape.
The quirky part is that this does not update "live" and does not support animation directly via the custom attribute.
So to see the update of the custom attribute into the shader, you use one of two methods:
Open ShaderFX and use Tools / Compute Graph to update the value.
Use MEL command: shaderfx -sfxnode "ShaderfxShader1" -update;
Knowing this limitation, you can see it is mostly there to allow some custom floats or textures to replace parts of the shader graph per object. It is not meant to completely replace regular Maya attribute that get put into the shader.
However, you can get pretty creative.
For example, lets say you want to animate between 5 different colors between 100 objects.
You can add 5 "color nodes" in the shaderfx graph and lerp (or if-statement switch) between them based on the primitive variable value found on the shape node.
This means you no longer have to worry about animating or updating the shape node attribute constantly. The primitive variable becomes more a "node id" that you can use to take different shading paths in the shader graph.