I am starting to learn Tkinter and have been creating new windows with new instances of Tk every time. I just read that that wasn't a good practice. If so, why? And how could this be done better? I have seen others create windows with Toplevel and Frame instances. What are the benefits/drawbacks of using these instead?
In case this makes a difference: The application that I am writing code for starts off with a login window and then proceeds to a second window is the entered password is correct.
Every tkinter program needs exactly one instance of Tk. Tkinter is a wrapper around an embedded tcl interpreter. Each instance of Tk gets its own copy of the interpreter, so two Tk instances have two different namespaces. 
If you need multiple windows, create one instance of Tk and then additional windows should be instances of Toplevel. 
While you can create, destroy, and recreate a root window, there's really no point. Instead, create the root window for the login screen, and then just delete the login screen widgets and replace them with your second window.
This becomes trivial if you make each of your "windows" a separate class that inherits from tk.Frame. Because tkinter will destroy all child widgets when a frame is destroyed, it's easy to switch from one "window" to another. Create an instance of LoginFrame and pack it in the root window. When they've input a correct password, destroy that instance, create an instance of MainWindow and pack that.
Tkinter Creating Multiple Windows - Use new Tk instance or Toplevel or Frame?