I realised I gave a quick definition of strong vs weak typing, but not dynamic vs static. Here's explanations for all four:
Strong typing: an object's type is immutable. To change the type, you must change the object's identity (i.e. create a new object). vs Weak typing: an object's type (and hence its behaviour) can be changed while leaving its identity untouched.
Python allows weak typing at the __class__ level (to support proxy objects and similar metaprogramming tools), but the underlying object model of the language is strongly typed.
Static typing: types are assigned not only to objects, but also to labels that refer to objects. The type of the label and the type of the object must match. Accordingly, variables must be explicitly associated with a type via variable declarations. vs Dynamic typing: types are assigned only to objects, and labels themselves are untyped. Accordingly, variables can be defined implicitly just by assigning a value to them. (Some otherwise statically typed languages include explicit support for dynamically typed references)
(An interest hybrid variant for static vs dynamic is an approach where labels *are* typed, but they acquire their type from the first value assigned to them. Automatic type inferencing can make static typing significantly less painful to work with, while still picking up most type errors at compile time. C++11 has gone that way with its introduction of "auto" type declarations for initialised types, where you explicitly tell the compiler "this variable is of the same type as the result of the initialiser").