Explore the PyQGIS API with dir() and help()
The PyQGIS API is enormous, the documentation is generated from C++, and the method you need is frequently one word away from the one you guessed. Introspection closes that gap faster than searching: the object in front of you knows exactly what it can do, and three lines in the console will tell you.
This recipe belongs to QGIS API Architecture. It covers listing an object's methods, filtering the inherited Qt noise, reading the signature and docstring of a method, checking which class you actually have, and translating what you find into the online documentation.
Prerequisites
- QGIS 3.34 LTR (bundled Python 3.12) or newer.
- The Python console open — see QGIS Python Console Basics.
- A loaded layer to experiment on, though any object will do.
List what an object can do
layer = iface.activeLayer()
methods = [name for name in dir(layer) if not name.startswith("_")]
print(len(methods))
print([name for name in methods if "field" in name.lower()])
Breakdown: dir() returns every attribute name the object exposes, including everything inherited from QgsMapLayer, QObject and Python itself — several hundred names, most of which are not what you want. Dropping names beginning with an underscore removes Python's internals immediately. The substring filter is the part that does the real work: you rarely know the exact method name, but you almost always know a word that will be in it. Searching for field, crs, select or commit narrows three hundred names to five in one line, and the answer is usually obvious from the list.
Read the signature and the documentation
help(layer.getFeatures)
help(QgsVectorLayer.setSubsetString)
Breakdown: help() on a bound method prints the signature and whatever docstring the bindings carry, which for PyQGIS is generated from the C++ header and includes the parameter and return types. Those types are the most valuable part: seeing that getFeatures() accepts a QgsFeatureRequest tells you where to look next far more directly than any tutorial. Calling help() on the class rather than an instance works identically and is handy when you have no object to hand. Where a method is overloaded — common in Qt-derived classes — you get every signature listed, which explains why a call that "should" work fails: you matched a different overload than you thought.
For a compact view in the console, signature from the standard library is often easier to read:
import inspect
print(inspect.signature(QgsVectorLayer.setSubsetString))
Breakdown: inspect.signature() gives one line rather than a page. It works for most PyQGIS bindings, and raises ValueError for the few built entirely in C++ without introspection metadata — in which case fall back to help(), which always has something to show.
Find out what you are actually holding
Half of all PyQGIS confusion is having a different class than you assumed.
print(type(layer))
print(type(layer).__mro__)
print(layer.__class__.__name__)
from qgis.core import QgsVectorLayer
print(isinstance(layer, QgsVectorLayer))
Breakdown: type() names the exact class, and the method resolution order shows the whole inheritance chain — which is how you discover that a QgsVectorLayer is a QgsMapLayer and therefore has everything documented on that page too. isinstance() is the right check in real code, because it accepts subclasses; comparing type(layer) == QgsVectorLayer fails for anything derived. This is also the fastest way to diagnose the classic "AttributeError on a valid layer" — the object is a QgsRasterLayer, or None, and the missing method was never going to be there.
Decode enumerations and constants
Many PyQGIS calls take or return integer-backed enumeration values, and a bare 2 in the console tells you nothing.
from qgis.core import QgsWkbTypes, Qgis
print(QgsWkbTypes.displayString(layer.wkbType()))
print([name for name in dir(Qgis.GeometryType) if not name.startswith("_")])
print(int(Qgis.GeometryType.Polygon), Qgis.GeometryType.Polygon.name)
Breakdown: Several QGIS enumerations ship a helper that turns a value into readable text — QgsWkbTypes.displayString() is the one you will use most, turning an opaque geometry type code into MultiPolygon. For the newer scoped enumerations under Qgis, listing the members shows every legal value, and each member has a name, which makes log messages readable. Printing the integer alongside is occasionally necessary when comparing against a value read from a file or a database column.
Map what you found onto the documentation
Introspection tells you a method exists; the API documentation tells you what it means. Two habits connect them.
Search the class, not the method. The QGIS API documentation is organised by class, and the class name from type() takes you straight to the page listing every method with its full C++ signature and, usually, a paragraph of explanation. Inherited members are on a separate tab, which is why the method resolution order is worth knowing.
Read the C++ types as Python ones. QString is str, QList<QgsFeature> is a Python list of features, a bool *ok output parameter usually becomes an extra value in a returned tuple, and a method documented as returning void returns None. Once that translation is automatic, the C++ documentation reads as Python documentation.
The one place introspection cannot help is behaviour: whether a method commits immediately, whether it invalidates an iterator, whether it is safe on a background thread. That is what the documentation and the wider guides here are for — QGIS API Architecture covers how the modules fit together, and QGIS Python Version Compatibility Guide covers what changed between releases.
QGIS version compatibility
| QGIS version | Python | Notes |
|---|---|---|
| 3.22 LTR | 3.9 | dir(), help() and inspect behave identically; some scoped enumerations under Qgis do not yet exist. |
| 3.28 LTR | 3.9 | Identical. |
| 3.34 LTR | 3.12 | Baseline for this page; most enumerations available in both the legacy and scoped forms. |
| 3.40 / 3.44 | 3.12 | Legacy enumeration aliases are being removed release by release — check with dir() rather than assuming an old name survives. |
Introspection is also the fastest way to handle a version difference: hasattr(layer, "someNewMethod") is a one-line compatibility check that keeps a plugin working across releases.
Troubleshooting
dir()returns hundreds of names. Filter by substring; almost nobody reads the full list.help()shows only the signature. The binding carries no docstring for that method. The class page in the API documentation will have the prose.inspect.signature()raisesValueError. A pure C++ binding without introspection metadata. Usehelp().- A method exists but raises
TypeErrorwhen called. You matched a different overload.help()lists them all; check the argument types. - An attribute is missing on an object that should have it. Check
type()— it is very oftenNone, a raster layer, or a layer tree node rather than the layer itself. - A name from a tutorial does not exist. It is QGIS 2 code, or a renamed method. Search
dir()for a distinctive word from the old name.
Conclusion
Three calls cover most exploration: dir() with a substring filter to find candidate methods, help() or inspect.signature() to learn how to call one, and type() with the method resolution order to confirm what you are holding. Decode enumerations with their display helpers, and use the class name to jump into the API documentation for the behaviour introspection cannot show you.
Frequently Asked Questions
Is the console's autocomplete the same thing?
It is dir() with a nicer interface, and it is often quicker. Introspection in code still matters for compatibility checks and for exploring objects you cannot easily type a name for.
Why do method names look like Qt rather than Python? PyQGIS wraps a C++ API, so it keeps camel-case names and Qt conventions. It is not idiomatic Python and trying to guess snake-case equivalents will not work.
How do I list the signals an object emits?
Filter dir() for names that are not callable methods, or check the class page in the documentation — signals are listed separately there. Connecting to one is covered in Connect Layer Signals in PyQGIS.
Can I introspect Processing algorithms this way? Their parameters are described by their own metadata rather than by Python attributes. Use the Processing tools described in Run a Processing Algorithm from a Script.
Does this work in a standalone script? Yes, identically — introspection needs nothing but the objects, so it works in a headless run as well as in the console.
Is there a faster way to find a class I only half remember the name of?
Filter the module rather than an object: [n for n in dir(qgis.core) if "renderer" in n.lower()] lists every core class with that word in its name, which usually surfaces the one you meant in a single line. The same trick on qgis.gui and qgis.analysis covers the rest of the API, and it is considerably quicker than searching documentation for a name you cannot spell.
Can I see the source of a PyQGIS method?
Rarely — most of it is compiled C++ with no Python source to show, so inspect.getsource() raises. Pure-Python parts of QGIS, such as the Processing framework and the plugin installer, do return their source, which makes them worth reading when you want to see how the framework itself calls the API.