Это вторичный loop или вы пытаетесь заменить главный целиком? В обеих случаях вы делаете неправильно.
1. Если это вторичный loop (который идет отдельно от основного), пишем в нужном шаблоне:
<?php
/**
* Secondary loop
*/
// Устанавливаем нужные аргументы
$args = array(
'cat' => 2,
'posts_per_page' => 3, // showposts - устаревший аргумент, с версии 2.1
);
// Выполняем запрос
$posts = new WP_Query( $args );
// Если посты найдены
if( $posts->have_posts() ) :
// Цикл для вывода постов
while( $posts->have_posts() ) :
// Закидываем текущий пост в цикле в глобальную переменную $post, чтобы работали все template tags
$posts->the_post();
// Тут вывод данных
endwhile; // Конец цикла
wp_reset_postdata(); // Обнуляем данные произвольного цикла и возвращаемся к основному loop
else :
// Посты не найдены
endif;
2. Если нужно модифицировать основной loop, пишем в functions.php:
// Функция, которую хукаем и модифицируем запрос
function my_modify_query( $query ) {
// Выполняем модификацию только если это основной запрос и мы находимся НЕ в админке
if( $query->is_main_query() && ! $query->is_admin() ) {
$query->set( 'cat', 2 );
$query->set( 'posts_per_page', 3 );
}
}
// Собственно хук в action 'pre_get_posts', который выполняется когда все параметры запроса установлены, но сам запрос в БД еще не ушел
add_action( 'pre_get_posts', 'my_modify_query' );
Колупаться с $paged нет необходимости, WordPress сам все корректно отработает.