Hook to switch the linter binaries in Emacs Lisp according to virtual environment
It would be a good idea to write a docstring for the defvar'd variables like linter-execs. The explanations in the post would make an excellent start, for example:

(defvar linter-execs '((flycheck-python-flake8-executable "bin/flake8")
                       (flycheck-python-pylint-executable "bin/pylint")
                       (flycheck-python-pycompile-executable "bin/python"))
  "The linter executables, as list of two-element lists. The
first element of an entry is the flycheck variable that contains
the path of a linter executable. The second element is the
relative path of the executable from within the venv.")

Writing (nth 0 exec) and (nth 1 exec) makes it hard to understand the meaning of these items of data. In Python you'd use tuple unpacking to give meaningful names to each element of the exec data structure, like this:

flycheck_var, path = exec


In Emacs Lisp you can use cl-destructuring-bind in a similar way:

(dolist (exec linter-execs)
  (cl-destructuring-bind (flycheck-var path) exec
    (let ((venv-linter-bin    (concat pyvenv-virtual-env path))
      ;; etc


But exec comes from a list that you are looping over, so you could use the cl-loop macro instead of dolist, and cl-loop has destructuring built in:

(cl-loop for (flycheck-var path) in linter-execs do
  (let ((venv-linter-bin    (concat pyvenv-virtual-env path))
    ;; etc


You'll need (require 'cl-macs) to use cl-destructuring-bind or cl-loop.
The two directories pyvenv-virtual-env and default-linter-venv-path are treated in exactly the same way: first we do (let ((Y (concat X path)) and then (file-exists-p Y) and finally (set flycheck-var Y). This repetition could be factored out into a loop:

(defun switch-linters ()
  "Switch linter executables to those in the current venv.

If the venv does not have any linter packages, then they will be
set to those in the `default-linter-venv-path` venv.  If these do
not exist, then the linter will be set to nil."
  (cl-loop with dirs = (list pyvenv-virtual-env default-linter-venv-path)
           for (flycheck-var path) in linter-execs
           do (set flycheck-var
                   (cl-loop for directory in dirs
                            for executable = (concat directory path)
                            if (file-exists-p executable) return executable)))

Instead of file-exists-p, you probably want to use file-executable-p.
Looking for a file in a list of directories is built into Emacs as the function locate-file. Using this, we get:

(defun switch-linters ()
  "Switch linter executables to those in the current venv.

If the venv does not have any linter packages, then they will be
set to those in the `default-linter-venv-path` venv.  If these do
not exist, then the linter will be set to nil."
  (cl-loop with dirs = (list pyvenv-virtual-env default-linter-venv-path)
           for (flycheck-var path) in linter-execs
           do (set flycheck-var (locate-file path dirs nil 'file-executable-p))))