Why don’t first_name and last_name appear when you print_r the WP_User object?

first_name and last_name are not the part of _users table it is user metadata. Therefore, user object does not have it. So when you access the property first_name user object has a PHP magic method __get()

/**
 * Magic method for accessing custom fields
 *
 * @since 3.3.0
 * @param string $key
 * @return mixed
 */
public function __get( $key ) {
    if ( 'id' == $key ) {
        _deprecated_argument( 'WP_User->id', '2.1', __( 'Use <code>WP_User->ID</code> instead.' ) );
        return $this->ID;
    }

    if ( isset( $this->data->$key ) ) {
        $value = $this->data->$key;
    } else {
        if ( isset( self::$back_compat_keys[ $key ] ) )
            $key = self::$back_compat_keys[ $key ];
        $value = get_user_meta( $this->ID, $key, true );
    }

    if ( $this->filter ) {
        $value = sanitize_user_field( $key, $value, $this->ID, $this->filter );
    }

    return $value;
}

Which query the metadata and return the value from _usermeta table.