Python for Architects: Practical Scripting in Rhino and Revit (With Real Examples)
A practical guide to Python scripting for architects - real code examples for Rhino and Revit, what to learn first, and when scripting saves time.
Go deeper with Archgyan Academy
Structured BIM and Revit learning paths for architects and students.
Python is the most useful programming language an architect can learn. Not because you need to become a programmer, but because 20 lines of Python can replace 2 hours of repetitive clicking in Rhino or Revit.
This guide covers practical scripting for both platforms - with real code you can copy, modify, and use on your projects today. No computer science background required.
Why Python (And Not Some Other Language)
Python is the default scripting language for both Rhino and Revit because:
| Factor | Python | Other Options |
|---|---|---|
| Rhino support | Built-in (RhinoPython, GhPython) | C#, VB.NET (more verbose) |
| Revit support | pyRevit, Dynamo Python nodes | C# add-ins (steeper learning curve) |
| Readability | Reads almost like English | C# requires more syntax knowledge |
| Learning curve | Days to write useful scripts | Weeks for equivalent C# |
| Community resources | Largest architecture scripting community | Fewer tutorials and examples |
| Transferable | Works in Blender, QGIS, data analysis | Platform-specific |
You don’t need to “learn Python” in the abstract. You need to learn enough Python to do specific things in Rhino or Revit. That’s a much smaller task.
Python in Rhino: Where to Write Code
Rhino offers three places to run Python:
1. Script Editor (Rhino 8+)
The new Script Editor in Rhino 8 replaces the old EditPythonScript window. Access it via Tools > Script Editor or type ScriptEditor in the command line.
- Full code editor with autocomplete
- Supports Python 3 (via CPython) and IronPython 2
- Can create commands that appear in Rhino’s toolbar
2. GhPython Component (Grasshopper)
Drag a GhPython Script component onto the Grasshopper canvas. This lets you mix visual programming with custom Python code - useful when a standard Grasshopper node doesn’t do what you need.
3. Command Line (Quick Scripts)
Type RunPythonScript to run a saved .py file. Good for scripts you’ve already written and want to execute quickly.
Rhino Python: 5 Practical Examples
Example 1: Select All Short Walls and Highlight Them
Find all surfaces below a certain height - useful for identifying parapet walls or low partitions:
import rhinoscriptsyntax as rs
threshold = 1200 # mm
all_surfaces = rs.ObjectsByType(8) # 8 = surface objects
short_ones = []
for srf in all_surfaces:
bbox = rs.BoundingBox(srf)
if bbox:
height = bbox[4][2] - bbox[0][2] # Z difference
if height < threshold:
short_ones.append(srf)
rs.SelectObjects(short_ones)
print(f"Found {len(short_ones)} surfaces shorter than {threshold}mm")
Example 2: Distribute Objects Along a Curve
Place copies of a block at equal intervals along any curve - for fencing, lighting, or trees:
import rhinoscriptsyntax as rs
curve = rs.GetObject("Select curve", 4)
block = rs.GetObject("Select object to distribute", 0)
count = rs.GetInteger("Number of copies", 10, 2, 500)
if curve and block and count:
params = [i / (count - 1.0) for i in range(count)]
for t in params:
pt = rs.EvaluateCurve(curve, rs.CurveParameter(curve, t))
rs.CopyObject(block, [0, 0, 0], pt)
Example 3: Export All Layer Names and Object Counts
Generate a quick audit of your model’s organisation:
import rhinoscriptsyntax as rs
layers = rs.LayerNames()
print("Layer Name | Object Count | Colour")
print("-" * 50)
for layer in sorted(layers):
objects = rs.ObjectsByLayer(layer)
count = len(objects) if objects else 0
colour = rs.LayerColor(layer)
if count > 0:
print(f"{layer} | {count} | {colour}")
Example 4: Create a Grid of Points
Generate a point grid for panel layouts, structural grids, or landscape planting:
import rhinoscriptsyntax as rs
cols = 10
rows = 8
spacing = 1500 # mm
for x in range(cols):
for y in range(rows):
rs.AddPoint(x * spacing, y * spacing, 0)
print(f"Created {cols * rows} points at {spacing}mm spacing")
Example 5: Rename All Blocks with a Prefix
Batch-rename blocks for file organisation:
import rhinoscriptsyntax as rs
prefix = "SITE_"
blocks = rs.BlockNames()
for name in blocks:
if not name.startswith(prefix):
rs.RenameBlock(name, prefix + name)
print(f"Renamed: {name} -> {prefix}{name}")
Python in Revit: The Options
Revit Python scripting works differently from Rhino. You can’t just open a script editor inside Revit - you need a bridge:
| Tool | What It Is | Best For |
|---|---|---|
| pyRevit | Free add-in with a Python script runner and pre-built tools | Custom toolbar buttons, automation scripts |
| Dynamo Python nodes | Python Script node inside Dynamo | Mixing visual programming with custom code |
| RevitPythonShell | Interactive Python console in Revit | Quick experiments, one-off queries |
| Revit API (C# add-in) | Full API access via compiled add-in | Production tools (not Python, but worth knowing about) |
Recommended starting point: Install pyRevit. It’s free, actively maintained, and comes with dozens of useful scripts you can use immediately while learning to write your own.
Revit Python: 5 Practical Examples
These examples use pyRevit’s environment (IronPython with Revit API access):
Example 1: List All Rooms with Areas
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = __revit__.ActiveUIDocument.Document
rooms = FilteredElementCollector(doc) \
.OfCategory(BuiltInCategory.OST_Rooms) \
.WhereElementIsNotElementType() \
.ToElements()
print("Room Number | Room Name | Area (sqm)")
print("-" * 50)
for room in rooms:
number = room.Number
name = room.get_Parameter(
BuiltInParameter.ROOM_NAME).AsString()
area = room.get_Parameter(
BuiltInParameter.ROOM_AREA).AsDouble() * 0.0929 # sqft to sqm
print(f"{number} | {name} | {area:.1f}")
Example 2: Rename Views by Adding a Prefix
from Autodesk.Revit.DB import (
FilteredElementCollector, Transaction, ViewType
)
doc = __revit__.ActiveUIDocument.Document
views = FilteredElementCollector(doc) \
.OfClass(View) \
.ToElements()
floor_plans = [v for v in views
if v.ViewType == ViewType.FloorPlan
and not v.IsTemplate]
t = Transaction(doc, "Rename Floor Plans")
t.Start()
for view in floor_plans:
old_name = view.Name
if not old_name.startswith("FP_"):
view.Name = "FP_" + old_name
t.Commit()
print(f"Renamed {len(floor_plans)} floor plans")
Example 3: Find Doors Missing Mark Values
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = __revit__.ActiveUIDocument.Document
doors = FilteredElementCollector(doc) \
.OfCategory(BuiltInCategory.OST_Doors) \
.WhereElementIsNotElementType() \
.ToElements()
missing = []
for door in doors:
mark = door.get_Parameter(
BuiltInParameter.ALL_MODEL_MARK).AsString()
if not mark or mark.strip() == "":
missing.append(door.Id)
print(f"Found {len(missing)} doors without Mark values")
print("Element IDs:", [str(m) for m in missing])
Example 4: Export Wall Schedule Data
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
import csv
doc = __revit__.ActiveUIDocument.Document
walls = FilteredElementCollector(doc) \
.OfCategory(BuiltInCategory.OST_Walls) \
.WhereElementIsNotElementType() \
.ToElements()
data = []
for wall in walls:
wall_type = wall.WallType.get_Parameter(
BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
length = wall.get_Parameter(
BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble() * 0.3048 # ft to m
data.append([wall_type, f"{length:.2f}"])
# Write to CSV
with open("C:/temp/wall_data.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["Wall Type", "Length (m)"])
writer.writerows(data)
print(f"Exported {len(data)} walls to CSV")
Example 5: Count Elements by Category
Quick model audit showing what’s in your project:
from Autodesk.Revit.DB import FilteredElementCollector, BuiltInCategory
doc = __revit__.ActiveUIDocument.Document
categories = [
("Walls", BuiltInCategory.OST_Walls),
("Doors", BuiltInCategory.OST_Doors),
("Windows", BuiltInCategory.OST_Windows),
("Rooms", BuiltInCategory.OST_Rooms),
("Floors", BuiltInCategory.OST_Floors),
]
print("Category | Count")
print("-" * 30)
for name, cat in categories:
count = FilteredElementCollector(doc) \
.OfCategory(cat) \
.WhereElementIsNotElementType() \
.GetElementCount()
print(f"{name} | {count}")
What to Learn First
Don’t try to learn “all of Python.” Learn just enough to be productive:
Week 1-2: Python Basics
| Concept | Why You Need It | Example |
|---|---|---|
| Variables | Store values | height = 3000 |
| Lists | Collections of items | rooms = ["Office", "Kitchen"] |
| For loops | Do something to every item | for room in rooms: |
| If/else | Make decisions | if area > 100: |
| Functions | Reusable code blocks | def get_area(width, length): |
| See results | print(f"Area: {area}") |
Week 3-4: Platform-Specific
For Rhino: Learn rhinoscriptsyntax - it’s a simplified wrapper around RhinoCommon. Start by automating something you do frequently (selecting objects by type, exporting data, creating geometry).
For Revit: Install pyRevit and read existing scripts in its library. Learn the FilteredElementCollector pattern - it’s the foundation of nearly every Revit script.
Month 2+: Build a Library
Start saving your scripts in a folder. After a month, you’ll have 5-10 scripts that save you real time. After six months, your personal script library becomes one of your most valuable professional tools.
When Scripting Saves Time (And When It Doesn’t)
| Scenario | Script? | Why |
|---|---|---|
| Rename 200 views | Yes | 5 minutes vs 2 hours |
| Extract room data to Excel | Yes | Repeatable, error-free |
| Create one section view | No | Faster manually |
| Generate a panel grid from parameters | Yes | Impossible to do accurately by hand at scale |
| Place one door | No | Just click |
| Audit model for missing data | Yes | Catches what manual checking misses |
| Quick design sketch | No | Scripting adds overhead |
| Process 50 DWG files | Yes | Batch operations are scripting’s strength |
The rule: if you’ll do the task more than 10 times, or if it involves processing data at scale, write a script. If it’s a one-off creative task, use the GUI.
Free Learning Resources
| Resource | Platform | What You Learn |
|---|---|---|
| Rhino Developer Docs | Rhino | RhinoPython and RhinoCommon reference |
| pyRevit Documentation | Revit | Script examples and API patterns |
| Automate the Boring Stuff (book) | General | Python fundamentals (free online) |
| The Dynamo Primer | Revit | Python nodes in Dynamo |
| McNeel Forum | Rhino | Community Q&A with code examples |
| Designalyze | Rhino | Tutorials specific to architecture |
Want to develop your computational design skills? The Archgyan Academy offers courses in Revit, BIM workflows, and design automation for architects and students.
Level up your skills
Ready to learn hands-on?
- Project-based Revit & BIM courses for architects
- Go from beginner to confident professional
- Video lessons you can follow at your own pace