This article is all about a specific leak, based on an impedance mismatch between the object model used in .NET and that used in Python: In C#, you can overload a method. A simple way to think about this is to realize that the name of the method includes its signature, the list of parameter (types) it takes. Go read a book if you want the gory details. Any book. I’m just going to get down to earth here and talk about a specific example:
The
Document.LoadFamily
method.The standard method for selecting a specific overload is just calling the function and having IronPython figure it out.
I guess the place to read up on how to call overloaded methods is here: http://ironpython.net/documentation/dotnet/dotnet.html#method-overloads. To quote:
When IronPython code calls an overloaded method, IronPython tries to select one of the overloads at runtime based on the number and type of arguments passed to the method, and also names of any keyword arguments.This works really well if the types passed in match the signatures of a specific method overload well. IronPython will try to automatically convert types, but will fail with a
TypeError
if more than one method overload matches.The
Document.LoadFamily
method is special in that one of its parameters is marked as out
in .NET - according to the standard IronPython documentation (REF) that should translate into a tuple of return values - and it does, if you know how. It is just non-intuitive - see this question on Stack Overflow:revitpythonshell provides two very similar methods to load a family.What is happening here is that the c# definitions of the method are:
So it seems like only the return values are different. I have tried to calling it in several different ways:LoadFamily(self: Document, filename:str) -> (bool, Family) LoadFamily(self: Document, filename:str) -> bool
(success, newFamily) = doc.LoadFamily(path)
success, newFamily = doc.LoadFamily(path)
o = doc.LoadFamily(path)
But I always just get a bool back. I want the Family too.
public bool LoadFamily(
string filename
)
andpublic bool LoadFamily(
string filename,
out Family family
)
The IronPython syntax candy for out
parameters, returning a tuple of results, can’t automatically be selected here, because calling LoadFamily
with just a string argument matches the first method overload. You can get at the overload you are looking for like this:
import clr
family = clr.Reference[Family]()
# family is now an Object reference (not set to an instance of an object!)
success = doc.LoadFamily(path, family) # explicitly choose the overload
# family is now a Revit Family object and can be used as you wish
This works by creating an object reference to pass into the function and the method overload resultion thingy now knows which one to look for.Working under the assumption that the list of overloads shown in the RPS help is the same order as they appear, you can also do this:
success, family = doc.LoadFamily.Overloads.Functions[0](path)
and that will, indeed, return a tuple (bool, Autodesk.Revit.DB.Family)
. I just don’t think you should be doing it that way, as it introduces a dependency on the order of the method overloads - I wouldn’t want that smell in my code…Note, that this has to happen inside a transaction, so a complete example might be:
import clr
t = Transaction(doc, 'loadfamily')
t.Start()
try:
family = clr.Reference[Family]()
success = doc.LoadFamily(path, family)
# do stuff with the family
t.Commit()
except:
t.Rollback()