This is a snippet from my low poly tree generator written in python for Maya. The script generates, scatters, rigs, and animates a low poly scene.

import maya.cmds as cmds
import maya.mel as mel
import random
import math
import functools
import mtoa.utils as mutils

  
class WorldGen:
      
    def __init__(self, pfx, iter):
        
        self.pfx = pfx
        self.iter = iter     
    
    def show(self):
        self.createMyLayout()
    def createMyLayout(self):
        self.pfxUI = self.pfx[:-1]
        if cmds.window("testUI", exists = True):
            cmds.deleteUI("testUI")
        self.window = cmds.window(title= "testUI", iconName = 'testUI', widthHeight = (500, 500))
        cmds.columnLayout(adjustableColumn = True, columnAlign = "left", rowSpacing = 20)
        self.clearScene = cmds.button(l = 'World_Clear', w = 300, h = 25, c = self.pfxUI + '.worldClear()')
        self.terrain(None)
        self.terrainSlider = cmds.intSliderGrp(l = 'Terrain_Seed', w = 300, h = 25,min = -50, max = 50, v = 0, s = 1, dc = 'empty')
        cmds.intSliderGrp(self.terrainSlider, e = True, dc = functools.partial(self.terrain, self.terrainSlider))
        self.genFunction = cmds.optionMenu(l = 'Select_Scattered_Obj')
        cmds.menuItem(l = 'Trees')
        cmds.menuItem(l = 'Reeds')
        cmds.menuItem(l = 'Rocks')
        self.scatterGenNum = cmds.intFieldGrp(l = 'Amount')
        self.scatterZoneMin = cmds.floatFieldGrp(l = 'Scatter_Min', pre = 3)
        self.scatterZoneMax = cmds.floatFieldGrp(l = 'Scatter_Max', pre = 3)
        self.scatterButton = cmds.button(l = 'Scatter', w = 300, h = 25, c = self.scatter)
        self.buildLights = cmds.button(l = 'Scene_Lights', w = 300, h = 25, c = self.buildLights)
        cmds.showWindow(self.window)
    
    def worldClear(self):
        ''' clears the scene '''
        if cmds.objExists(self.pfx + '*'):
            cmds.delete(self.pfx + '*')
        mel.eval('hyperShadePanelMenuCommand("hyperShadePanel1", "deleteUnusedNodes");')
        
    def getSliderValue(self,ctrlName):
        value = cmds.intSliderGrp(ctrlName, q = 1, value = 1)
        return value
    def clamp(self, num, min, max): return min if num < min else max if num > max else num    
    
    def buildLights(self, *args):
        ''' builds an hdri dome '''
        
        mutils.createLocator("aiSkyDomeLight", asLight = True)
        cmds.rename('aiSkyDomeLight1', self.pfx + 'skyDome')
        exr = cmds.shadingNode('file', asTexture = 1, isColorManaged = 1, n = self.pfx + 'skyDomeHDRI')
        placeHDRI = cmds.shadingNode('place2dTexture', asUtility = 1)
        self.project = cmds.workspace(q = 1, rd = 1)
        self.hdriPath = cmds.fileDialog2(fileFilter = '*.exr', dir = self.project, cap = 'Select_HDRI')[0]
        cmds.setAttr(exr + '.fileTextureName', self.hdriPath, type = 'string')
        cmds.connectAttr(placeHDRI + '.outUV', exr + '.uvCoord', force = 1)
        cmds.connectAttr(exr + '.outColor', self.pfx + 'skyDome.color')
        
    
    def bottomPivot(self, object):
        ''' bottoms the pivot and snaps object to the origin '''
        cmds.xform(object, cp=1)
        boundingBox = cmds.xform(object, q = 1, bb = 1, ws = 1)
        xMin, yMin, zMin, xMax, yMax, zMax = boundingBox
        cmds.move(yMin, [object + '.scalePivot', object + '.rotatePivot'], y = 1, absolute = 1)
        cmds.move(yMin*-1, object, r = 1, y = 1)
    
    def squashDeform(self, object, sqMultiplier):
        ''' deforms an object'''
        sqFactor = round(random.random()/sqMultiplier, 2)
        sqFactor = sqFactor * -1
        flFactorX = 1 + random.random()
        flFactorZ = 1 + random.random()
        cmds.select(object)
        cmds.nonLinear(typ = 'squash', factor = sqFactor)
        cmds.select(object)
        cmds.nonLinear(typ = 'flare', startFlareX = flFactorX, startFlareZ = flFactorZ)
    def unwrap(self, object):
        cmds.select(object)
        cmds.polyPlanarProjection(object + '.f[:]', md = 'x')
    
    def shadeObj(self, object, name, rgb, randomArg, rampArg):
        '''Assign shaders to object'''
        if not cmds.objExists(name + '_SN*'):
            shader = self.createShader(name + '_SN', rgb, randomArg, rampArg)
            if randomArg:
                pick = random.randint(0,4)
                assignSurface = shader[pick] 
            else:
                assignSurface = shader
        else:
            if randomArg:
                pick = random.randint(0,4)
                shader = name + '_SN_' + str(pick)
                assignSurface = shader
            else:
                shader = name + '_SN'
                assignSurface = shader
        cmds.select(object)
        cmds.hyperShade(a = assignSurface)

        return assignSurface
    
    def createShader(self, shaderName, rgb, randomArg, rampArg):
        ''' creates an Arnold shader'''
        if randomArg:
            shaderOptionsList = []
            for i in range(0, 5):
                surfaceNode = cmds.shadingNode('aiStandardSurface', asShader = 1, n = shaderName + '_' + str(i))
                offsetRGB = random.uniform(-.010, .010)
                rgb = [value + offsetRGB for value in rgb]
                if rampArg:
                    ramp = cmds.shadingNode('ramp', asTexture = 1, n = shaderName + '_rampTx_' + str(i))
                    cmds.setAttr(ramp + '.colorEntryList[0].color', rgb[0], rgb[1], rgb[2])
                    cmds.setAttr(ramp + '.colorEntryList[1].color', self.clamp(rgb[0] - .150, 0.000, 1.000), self.clamp(rgb[1] - .150, 0.000, 1.000), self.clamp(rgb[2] - .150, 0.000, 1.000)) 
                    cmds.setAttr(ramp + '.colorEntryList[1].position', 1)
                    cmds.connectAttr(ramp + '.outColor', surfaceNode + '.baseColor')
                else:
                    constantNode = cmds.shadingNode('colorConstant', asUtility = 1, n = shaderName + '_constant_' + str(i))
                    cmds.setAttr(constantNode + '.inColor', rgb[0], rgb[1], rgb[2])
                    cmds.connectAttr(constantNode + '.outColor', surfaceNode + '.baseColor')
                cmds.setAttr(surfaceNode + '.specularRoughness', .65)
                shaderOptionsList.append(surfaceNode)
            return shaderOptionsList
        else:
            surfaceNode = cmds.shadingNode('aiStandardSurface', asShader = 1, n = shaderName)
            if rampArg:
                ramp = cmds.shadingNode('ramp', asTexture = 1, n = shaderName + '_rampTx')
                cmds.setAttr(ramp + '.colorEntryList[0].color', rgb[0], rgb[1], rgb[2])
                cmds.setAttr(ramp + '.colorEntryList[1].color', self.clamp(rgb[0] - .150, 0.000, 1.000), self.clamp(rgb[1] - .150, 0.000, 1.000), self.clamp(rgb[2] - .150, 0.000, 1.000)) 
                cmds.setAttr(ramp + '.colorEntryList[1].position', 1)
                cmds.connectAttr(ramp + '.outColor', surfaceNode + '.baseColor')
            else:
                constantNode = cmds.shadingNode('colorConstant', asUtility = 1, n = shaderName + '_constant')
                cmds.setAttr(constantNode + '.inColor', rgb[0], rgb[1], rgb[2])
                cmds.connectAttr(constantNode + '.outColor', surfaceNode + '.baseColor')
            cmds.setAttr(surfaceNode + '.specularRoughness', .65)
            return surfaceNode
    def average(self, list):
        sum = 0.000
        for i in list:
            sum += i
        avg = round(sum / len(list), 3)
        return avg
        
    #---------------------------------------------------------------------------------------------------------
    def leafGen(self, res, gName, pre):
        '''generates a leaf clump'''
        var = random.randint(10,15)
        storeVar = var
        rInnit = random.uniform(6.01, 7.01)
        primaryClump = cmds.polySphere(n = pre + 'outLeaf_0', r =  rInnit, sx = res, sy = res)
        self.squashDeform(primaryClump, 2)        
        for i in range(0, storeVar):       
            radius = random.uniform(5.01, 6.50)
            secClump = cmds.polySphere(n = 'leafClump {}'.format(i), r = radius, sx = res, sy = res)
            boundingBox = cmds.xform(primaryClump, q =1, bb = 1, ws = 1)
            boundingBox = [round(value, 2) for value in boundingBox]
            xMinNew = boundingBox[0] / 1.25
            xMinNew = boundingBox[3] / 1.25
            yMinNew = boundingBox[1] * .5
            yMaxNew = boundingBox[4] * .5
            zMinNew = boundingBox[2] / 1.25
            zMaxNew = boundingBox[5] / 1.25
            posX = random.uniform(xMinNew, yMinNew)
            posY = random.uniform(yMinNew, yMaxNew)
            posZ = random.uniform(zMinNew, zMaxNew)
            cmds.move(posX,posY,posZ)
            self.squashDeform(secClump, 3)
            cmds.select('leaf*')
            cmds.rename(pre + 'outLeaf_1')
        cmds.select(pre + 'outLeaf_*')
        cmds.DeleteHistory()
        cmds.select(pre + 'outLeaf_*')
        group = cmds.group(n = gName)
        return group
....