user60405: You can extract

the low-order (rightmost) byte of a variable

or

the high-order (leftmost) byte of a word

with the functions lowByte() and highByte() respectively (the quotes are from the Arduino Reference).
Does Arduino provide a way to extract any byte from a number with a similar function?
And does it provide a way to set any individual bytes of a number (which would be the counterparts to these functions, if they exist)?

user7508: The header Arduino.h defines the macro word(high_byte, low_byte). The
resulting value is an uint16_t.

user59576: There are two alternative methods, and they can both be easily used "in reverse" as well:
Unions
union byte_extract {
    uint32_t ival;
    struct {
        uint8_t byte_0; // least significant byte
        uint8_t byte_1;
        uint8_t byte_2;
        uint8_t byte_3; // most significant byte
    } bval;
};

... later ...

union byte_extract x;

x.ival = 65536 * 99; // change to whatever you want
Serial.println(x.bval.byte_2); //3rd byte

Unions are a way to define multiple possible ways to interpret a piece of memory, usually to save memory when you know something can't be 2 things at once. Strictly speaking it is not recommended to use them in this way but it works.
Pointer arithmetic
uint32_t *xaddr;
uint8_t *xbyteaddr;

uint32_t x = 65536 * 99; // change to whatever you want
xaddr = &x;
xbyteaddr = (uint8_t *) xaddr;

Serial.println(xbyteaddr[2]); //3rd byte

Memory addresses (on Arduino as well as on PCs) refer to the first byte of a variable.  If you have an (uint32_t *) you will read 4 bytes starting at that address. But if you have a (uint8_t *) or a (char *) you will only read 1 byte. You can shift this pointer-to-uint8_t forwards by as many bytes as you want.
Apologies if this doesn't quite work on Arduino, I have only tested it on PC.

user35430: Any more sophisticated byte exchange can be done with binary operators.
Extracting information
You'll need some constants like
MASK         SHIFT                                    
0xFF000000   24       (first byte, most significant)
0x00FF0000   16       (second byte)
0x0000FF00    8       (third byte)
0x000000FF    0       (last byte, least significant)

You would binary AND the original value with MASK 
     0x12345678
AND  0x00FF0000
 =   0x00340000

and then bit shift it left, e.g.
   0x00340000
>> 16
 = 0x00000034

So from the original value 0x12345678 you have gotten 0x34.
Setting information
The opposite direction is also possible with the same constants but opposite operators and in opposite order:
   0x00000034
<< 16
 = 0x00340000

and then
   0x12005678
OR 0x00340000
 = 0x12345678

Note that the OR operation only works reliably if the corresponding positions are 0x00. That's fine with a starting value of 0x00000000.
If you don't know that or you want to process an arbitrary number, you can introduce a step in between
    0x12??5678   (?? could be anything)
AND 0xFF00FFFF   (which is the inverse of 0x00FF0000 and can be expressed as ~0x00FF0000)
 =  0x12005678   (whatever ?? was, it'll be cleared out)

Actual Code
Tested on Arduino Uno. The long data type is 32 bits or 4 bytes. 
The code here may not be the most efficient. Advanced developers would likely not use MASK and SHIFT as arrays like this. This answer focuses more on the educational point.
long MASK[]  = {0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF};
byte SHIFT[] = {24        , 16        , 8         , 0};

// Extracts a byte from a larger data type
// value: larger data type to extract a byte from
// position: 0 based number, 0 = MSB, 3 = LSB
byte getByteAt(long value, byte position)
{
  long result = value & MASK[position];  // binary AND
  result = result >> SHIFT[position];    // Shift right, moving all bits
  byte resultAsByte = (byte) result;     // Convert to an actual byte
  return resultAsByte;  
}

// Sets a byte in a larger data type
// value: larger data type where to set the byte
// position: 0 based number, 0 = MSB, 3 = LSB
// newPartialValue: the byte to be inserted
long setByteAt(long value, byte position, byte newPartialValue)
{
  long result = value & ~MASK[position];                       // clear the affected byte so that it is 0x00
  long valueToSet = (long) newPartialValue << SHIFT[position]; // Shift left, moving all bits
  result = result | valueToSet;                                // binary OR
  return result;  
}

void setup() {
  Serial.begin(9600);
  Serial.println(getByteAt(0x12345678, 1));       // Prints 52, which is 0x34
  Serial.println(setByteAt(0x87654321, 1, 0xBB)); // Prints -2017770719 which is 0x87BB4321
}

void loop() {

}


60405: <p>You can extract</p>

<blockquote>
  <p>the low-order (rightmost) byte of a variable</p>
</blockquote>

<p>or</p>

<blockquote>
  <p>the high-order (leftmost) byte of a word</p>
</blockquote>

<p>with the functions <code>lowByte()</code> and <code>highByte()</code> respectively (the quotes are from the Arduino Reference).</p>

<p>Does Arduino provide a way to extract <em>any</em> byte from a number with a similar function?</p>

<p>And does it provide a way to set <em>any</em> individual bytes of a number (which would be the counterparts to these functions, if they exist)?</p>
 7508: <p>The header Arduino.h defines the macro <code>word(high_byte, low_byte)</code>. The
resulting value is an <code>uint16_t</code>.</p>
 59576: <p>There are two alternative methods, and they can both be easily used "in reverse" as well:</p>

<h3>Unions</h3>

<pre><code>union byte_extract {
    uint32_t ival;
    struct {
        uint8_t byte_0; // least significant byte
        uint8_t byte_1;
        uint8_t byte_2;
        uint8_t byte_3; // most significant byte
    } bval;
};

... later ...

union byte_extract x;

x.ival = 65536 * 99; // change to whatever you want
Serial.println(x.bval.byte_2); //3rd byte
</code></pre>

<p>Unions are a way to define multiple possible ways to interpret a piece of memory, usually to save memory when you know something can't be 2 things at once. Strictly speaking it is not recommended to use them in this way but it works.</p>

<h3>Pointer arithmetic</h3>

<pre><code>uint32_t *xaddr;
uint8_t *xbyteaddr;

uint32_t x = 65536 * 99; // change to whatever you want
xaddr = &amp;x;
xbyteaddr = (uint8_t *) xaddr;

Serial.println(xbyteaddr[2]); //3rd byte
</code></pre>

<p>Memory addresses (on Arduino as well as on PCs) refer to the first byte of a variable.  If you have an (uint32_t *) you will read 4 bytes starting at that address. But if you have a (uint8_t *) or a (char *) you will only read 1 byte. You can shift this pointer-to-uint8_t forwards by as many bytes as you want.</p>

<p>Apologies if this doesn't quite work on Arduino, I have only tested it on PC.</p>
 35430: <p>Any more sophisticated byte exchange can be done with binary operators.</p>

<h2>Extracting information</h2>

<p>You'll need some constants like</p>

<pre><code>MASK         SHIFT                                    
0xFF000000   24       (first byte, most significant)
0x00FF0000   16       (second byte)
0x0000FF00    8       (third byte)
0x000000FF    0       (last byte, least significant)
</code></pre>

<p>You would binary AND the original value with MASK </p>

<pre><code>     0x12345678
AND  0x00FF0000
 =   0x00340000
</code></pre>

<p>and then bit shift it left, e.g.</p>

<pre><code>   0x00340000
&gt;&gt; 16
 = 0x00000034
</code></pre>

<p>So from the original value 0x12345678 you have gotten 0x34.</p>

<h2>Setting information</h2>

<p>The opposite direction is also possible with the same constants but opposite operators and in opposite order:</p>

<pre><code>   0x00000034
&lt;&lt; 16
 = 0x00340000
</code></pre>

<p>and then</p>

<pre><code>   0x12005678
OR 0x00340000
 = 0x12345678
</code></pre>

<p>Note that the OR operation only works reliably if the corresponding positions are 0x00. That's fine with a starting value of 0x00000000.</p>

<p>If you don't know that or you want to process an arbitrary number, you can introduce a step in between</p>

<pre><code>    0x12??5678   (?? could be anything)
AND 0xFF00FFFF   (which is the inverse of 0x00FF0000 and can be expressed as ~0x00FF0000)
 =  0x12005678   (whatever ?? was, it'll be cleared out)
</code></pre>

<h2>Actual Code</h2>

<p>Tested on Arduino Uno. The <code>long</code> data type is 32 bits or 4 bytes. </p>

<p>The code here may not be the most efficient. Advanced developers would likely not use <code>MASK</code> and <code>SHIFT</code> as arrays like this. This answer focuses more on the educational point.</p>

<pre><code>long MASK[]  = {0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF};
byte SHIFT[] = {24        , 16        , 8         , 0};

// Extracts a byte from a larger data type
// value: larger data type to extract a byte from
// position: 0 based number, 0 = MSB, 3 = LSB
byte getByteAt(long value, byte position)
{
  long result = value &amp; MASK[position];  // binary AND
  result = result &gt;&gt; SHIFT[position];    // Shift right, moving all bits
  byte resultAsByte = (byte) result;     // Convert to an actual byte
  return resultAsByte;  
}

// Sets a byte in a larger data type
// value: larger data type where to set the byte
// position: 0 based number, 0 = MSB, 3 = LSB
// newPartialValue: the byte to be inserted
long setByteAt(long value, byte position, byte newPartialValue)
{
  long result = value &amp; ~MASK[position];                       // clear the affected byte so that it is 0x00
  long valueToSet = (long) newPartialValue &lt;&lt; SHIFT[position]; // Shift left, moving all bits
  result = result | valueToSet;                                // binary OR
  return result;  
}

void setup() {
  Serial.begin(9600);
  Serial.println(getByteAt(0x12345678, 1));       // Prints 52, which is 0x34
  Serial.println(setByteAt(0x87654321, 1, 0xBB)); // Prints -2017770719 which is 0x87BB4321
}

void loop() {

}
</code></pre>
