Keen readers of this blog will know from time to time I give Python COM gateway classes which are callable from VBA. Up until now I do not think I have given an example of a class that has attributes (or a constructor). Time to rectify this.
Open Visual Studio 2017 and add a new Python Applciation project and then copy and paste in the code below. Save the file, calling it whatever you want mine is called PythonExampleClassWithAttributes.py
- class ExampleComClassWithAttributes(object):
- _reg_clsid_ = "{EF38F5B8-6E86-4D8E-A93E-C5B6C05CF378}"
- _reg_progid_ = 'PythonLib1.ExampleComClassWithAttributes'
- _public_methods_ = ['AMethod']
- _public_attrs_ = ['Foo','ws','ReadOnlyProp']
- _readonly_attrs_ = ['ReadOnlyProp']
- def __init__(self):
- self.ReadOnlyProp = "Not writable!"
- def AMethod(self):
- return "AMethod returns"
- def TestExampleComClassWithAttributes():
- # This test routine gives each feature a spin
- # (except ws for which see VBA client example)
- test = ExampleComClassWithAttributes()
- test.Foo = 54
- print(test.Foo)
- print(test.AMethod())
- print(test.ReadOnlyProp)
- def RegisterThis():
- print("Registering COM servers...")
- import win32com.server.register
- win32com.server.register.UseCommandLine(ExampleComClassWithAttributes)
- if __name__ == '__main__':
- RegisterThis()
- TestExampleComClassWithAttributes()
Running the above code from Visual Studio 2017 should give a command window output of the following...
Registering COM servers... Registered: PythonLib1.ExampleComClassWithAttributes 54 AMethod returns Not writable! Press any key to continue . . .
_public_attrs_ and _readonly_attrs_
the new features in this code are the two class level attributes of _public_attrs_ and _readonly_attrs_. _public_attrs_ is an array of attributes that are to be exposed by Python to COM. That's all you need to get attributes up and running; very economical with lines of code!
You may be wondering where are the property procedures? And also wondering that without them how do you make properties read only? To make properties read only you add them to the _readonly_attrs_ array. In the above code the property named "ReadOnlyProp" is read-only, attempting to write to that property will error.
def __init__(self)
I don't think I have given a Python class constructor example on this blog before either. In the above code I give one, it is the block of code headed def __init__(self) on lines 9-10. Because I made "ReadOnlyProp" read-only I somehow need to set the value, in the code above I set it in the constructor.
Client VBA Code
So here is the client VBA code which can be pasted into a standard module.
modExampleComClassWithAttrs Standard Module
- Option Explicit
- Sub VBATestExampleComClassWithAttributes()
- Dim objExampleWithAttrs As Object
- Set objExampleWithAttrs = VBA.CreateObject("PythonLib1.ExampleComClassWithAttributes")
- Debug.Print objExampleWithAttrs.AMethod
- Set objExampleWithAttrs.ws = ThisWorkbook.Worksheets.Item(1)
- Debug.Print objExampleWithAttrs.ws.name
- objExampleWithAttrs.Foo = 54
- Debug.Print objExampleWithAttrs.Foo
- Debug.Print objExampleWithAttrs.ReadOnlyProp
- End Sub
So one feature above not found in the Python test code is that I am setting one attribute 'ws' to be a object reference to a worksheet and so I need the Set keyword, notice how in the Python code I do not need to give this. Python is less fussy it seems, but with it the responsibility for the programmer to take care and test their code.
Final Thoughts
I have to say I feel quite liberated to be able to define a class that can carry state, i.e. has attributes, without having to add a class module for each class to the VBA project. In my university computer sciences courses I was firmly taught object orientation (OO). And to do good OO can require many classes, some of them potentially quite small. The Python files can hold as many classes as you need. No need to fill your VBA projects up with mini-classes.