Un article qui était resté au fond du tiroir sur des opérations de listes Python
#DynamoBIM #Python #AutodeskExpertElite #AutodeskCommunity 
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
Voici une série d'exemples d'opération sur des listes qui pourront être utiles aux débutants, peut-être même aux plus confirmés.
- Aplatir une liste
# Flatten Example 1
import sys
#sys.setrecursionlimit(20000)
def flattenGen(*args):
for x in args:
if hasattr(x, "__iter__") and not isinstance(x, str):
for y in flattenGen(*x) : yield y
else: yield x
def flattenList(l):
if hasattr(l, "__iter__") and not isinstance(l, (str, bytes, System.String)):
return [item for sublist in l for item in flattenList(sublist)]
else:
return [l]
test_lst = ['1','2','3',['4','5','6'],['6','7',['10','20']],'5','6']
test_lst2 = [1,2,3,[4,5,6],[6,7]]
for x in flattenGen(test_lst):
print (x)
# Flatten Example 2
import re
L = [[[1, 2, 3], [4, 5]], 6]
flattened_list = re.sub("[[]]", "", str(L)).replace(" ", "").split(",")
new_list = list(map(int, flattened_list))
print(new_list)
# Flatten Example 3 with PythonNet3 or IronPython
import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
import traceback
def FlattenWithLinq(*args):
for x in args:
if hasattr(x, "__iter__") and not isinstance(x, (str, System.String)):
iList = List[System.Object](x)
iList = iList.SelectMany[System.Object, System.Object](
System.Func[System.Object, List[System.Object]](
lambda p : FlattenWithLinq(p) if hasattr(p, "__iter__") \
and not isinstance(p, (str, System.String)) else [p])).ToList()
yield from iList
else:
yield x
iList = IN[0]
test = list(FlattenWithLinq(iList))
OUT = test
-
Découper (split) une liste
my_list=[1,2,3,1,5,6,7,5,9,9]
# split a list by maximum items in sublist
n=4 # number items by sublist
final = [my_list[i:i+n] for i in range(0, len(my_list), n)]
print (final)
# split a list by count of sublist
n=4 # number of sublist
import numpy as np
split_list = np.array_split(my_list, n)
final2 = [list(arr) for arr in split_list]
print (final2)
- Ordonner une liste suivant plusieurs clés
# sort by multiple keys
s = sorted(s, key = lambda x: (x[1], x[2]))
s = sorted(my_list, key=lambda i: ( criteria_1(i), criteria_2(i) ), reverse=True)s = sorted(my_list, key=lambda i: ( criteria_1(i), criteria_2(i) ), reverse=True)
- Transposer une liste
Exemple 1
Penser à utiliser les méthodes .Net dans ce cas
Remplacer une partie de chaine de caractère (Regex)
dataEnteringNode = IN
#transpose if list contain sublist
if all(hasattr(i, '__iter__') for i in dataEnteringNode):
dataEnteringNode = [i for i in zip(*dataEnteringNode)]
else:
dataEnteringNode = [dataEnteringNode]
Exemple 2
l=[[1,2,3],[4,5,6],[7,8,9]]
[list(i) for i in zip(*l)]
#result
#[[1, 4, 7], [2, 5, 8], [3, 6, 9]]
- Ordonner une liste de chaine de caractère avec des nombres (Natural Sort V1)
import re
def natural_sort(l):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
return sorted(l, key=alphanum_key)
- Ordonner une liste de chaine de caractère avec des nombres (Natural Sort V2)
import re
lst = ["var1", "var3", "var11", "var4"]
def sort_Str_Numb(lst):
lst.sort(key = lambda x: re.search(r'.*(\d)', x).group(1))
return lst
- Ordonner une liste suivant une liste d'index
a = ['c', 'd', 'B', 'a', 'e']
b = ['a001', 'B002', 'c003', 'd004', 'e005']
c = sorted(b, key = lambda x: a.index(x[0])) # ['c003', 'd004', 'b002', 'a001', 'e005']
#
#example to reverse values
textToReverse = "ZDA.01/ZDM.01"
sortFilter = ["ZDM", "ZDA"]
lstTemp = textToReverse.split("/")
lstTemp.sort(key = lambda x : sortFilter.index(x[:2]))
goodText = "/".join(lstTemp)
Parfois, il peut s'avérer que
list.index()
ne fonctionne pas, car
l'item recherché ne se trouve pas dans la liste.Penser à utiliser les méthodes .Net dans ce cas
Ici
IndexOf()
retourne -1 quand l'index n'est pas trouvé au lieu d'une erreur
import sys
import clr
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
a = List[System.String](['c', 'd', 'B', 'a', 'e', 'k'])
b = ['a001', 'z002', 'B002', 'c003', 'd004', 'e005', ]
c = sorted(b, key = lambda x: float('inf') if a.IndexOf(x[0]) == -1 else a.IndexOf(x[0]) )
# ['c003', 'd004', 'b002', 'a001', 'e005', 'z002']
OUT = c
- Grouper une liste avec itertools
la liste nécessite d'être ordonnée avant l'application de
"groupby"
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import itertools
input_lst = IN[0]
input_lst.sort(key=lambda x : x.EndPoint.Z)
groups = [ list(_group) for (_key, _group) in itertools.groupby(input_lst, lambda x : x.EndPoint.Z)]
OUT = groups
- Grouper une liste avec un dictionnaire
La méthode de base avec (Python 2 et 3)
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
from System.Collections.Generic import Dictionary
pyDict = {}
for lin in IN[0]:
mykey = lin.EndPoint.Z.ToString()
if mykey not in pyDict:
pyDict[mykey] = [lin]
else:
pyDict[mykey].append(lin)
OUT = Dictionary[str, object](pyDict)
La méthode avec defaultdict (Python 2 et 3)
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
from collections import defaultdict
from System.Collections.Generic import Dictionary
pyDict = defaultdict(list)
for lin in IN[0]:
mykey = lin.EndPoint.Z.ToString()
pyDict[mykey].append(lin)
OUT = Dictionary[str, object](pyDict)
La méthode avec setdefault() (Python 3)
import sys
import clr
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
from System.Collections.Generic import Dictionary
pyDict = {}
for lin in IN[0]:
mykey = lin.EndPoint.Z.ToString()
pyDict.setdefault(mykey, []).append(lin)
OUT = Dictionary[str, object](pyDict)
- Grouper une liste avec Linq GroupBy (.Net)
compatible avec Ironpython2, IronPython3, DSPythonNet3
import sys
import clr
import System
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
lstPoints = List[Point](IN[0])
resultGroup = lstPoints.GroupBy[Point, int](System.Func[Point, int](lambda p : int(p.Z)))
OUT = resultGroup
import re
varrr = "3f3839cc-a118-4525-91dc-ccfac34ef90d-000aedd9:RVTLINK/3f3839cc-a118-4525-91dc-ccfac34ef90d-000aedd8:274326:6:SURFACE"
var22 = re.sub(r"(^.*:)RVTLINK.*(:.*:.*:.*)", r"\g<1>0:RVTLINK\g<2>", varrr)
print(var22)
- Ordonner une liste de Point suivant une autre liste de Point
import sys
import clr
import random
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
def sortFunc(ptb):
global lstPtsA
ptB = Point.ByCoordinates(ptb.X, ptb.Y, 0)
for idx, pta in enumerate(lstPtsA):
ptA = Point.ByCoordinates(pta.X, pta.Y, 0)
if ptA.DistanceTo(ptB) < 0.01:
return idx
return float('inf')
pt1 = Point.ByCoordinates(-1385.574, -173.02607, 0)
pt2 = Point.ByCoordinates(-1385.57404, -98.026, 0)
pt3 = Point.ByCoordinates(-1445.574, -173.02608, 0)
pt4 = Point.ByCoordinates(-1445.57405, -98.026, 0)
pt5 = Point.ByCoordinates(-1445.574, -173.02609, 400)
pt6 = Point.ByCoordinates(-1445.574, -98.026, 400)
pt7 = Point.ByCoordinates(-1385.57403, -173.02609, 400)
pt8 = Point.ByCoordinates(-1385.57402, -98.026, 400)
lstPts = [pt1, pt2, pt3, pt4, pt5, pt6, pt7, pt8]
# shuffle
lstPts = random.shuffle(lstPts)
#lstPts = IN[0]
lstPts = sorted(lstPts, key = lambda x : x.Z)
lstPtsA = lstPts[: len(lstPts) / 2]
lstPtsB = lstPts[len(lstPts) / 2 : ]
# sort lstPtsB by lstPtsA coordinnates
lstPtsB.sort(key = lambda x : sortFunc(x))
# draws lines
DSlines = [Line.ByStartPointEndPoint(pta, ptb) for pta, ptb in zip(lstPtsA, lstPtsB)]
OUT = DSlines
- Exemple d'un générateur d'Objet Python (anonyme)
def genClap(nbrClap):
hand = type('',(object,),{"Clap": lambda self : print("clap! " + self.name), "name" : "MySelf"})()
for i in range(nbrClap):
yield hand
for hand in genClap(10):
hand.Clap()
## OR
def genClap(nbrClap):
hand = type('',(),{"Clap": lambda self : print("Clap! " + self.name), "name" : "My_Name"})()
for i in range(nbrClap):
yield hand
for hand in genClap(10):
hand.Clap()
.
-
Appliquer une fonction sur une multiple liste en conservant la
structure d'entrée
-
Une méthode similaire de 'Find' ou 'FirstOrDefault' (.Net) avec
Python
import clr
import sys
import System
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
from Autodesk.Revit.DB.Electrical import *
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *
clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.Elements)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
Output = []
Elec = ElectricalSetting.GetElectricalSettings(doc)
# equivalent of 'Find' or FirstOrDefault method (.Net Collection)
WireMaT = next((x for x in Elec.WireMaterialTypes if x.ToDSType(False).Name == IN[0]), None)
if WireMaT is not None:
Names = "NEC-210-3A"
TransactionManager.Instance.EnsureInTransaction(doc)
NewMaterial = Elec.AddWireMaterialType(Names, WireMaT)
Output.append(NewMaterial.ToDSType(True).Name)
TransactionManager.Instance.TransactionTaskDone()
OUT = Output
-
Convertir des coordonnées Autocad en liste de Point
def AcDb2DPLineToDS(obj):
coords = iter(obj.Coordinates)
DSPoints = [Point.ByCoordinates(x,y,0) for x, y in zip(coords, coords)]
return PolyCurve.ByPoints(DSPoints, True)
- Incrémenter la partie finale d'un texte
import re
hostname01 = "hostname10"
print(re.sub(r'(\d+)', lambda x: str(int(x.group(0)) + 1).zfill(2), hostname01))
- la méthode 'drange' avec IronPython
Équivalent à
numpy.arange(0,1,0.1)
avec numpy
import sys
import clr
def drange(start, end, step):
i = start
while round(i, 10) < end:
yield i
i += step
OUT = list(drange(IN[0], IN[1], IN[2]))
# OR
# Version version reversed increment
def drange(start, end, step):
i = start
while eval("round(i, 10) {} end".format("<=" if step > 0 else ">=")):
yield i
i += step
print(list(drange(0, 1, 0.1)))
print(list(drange(1, 0, -0.1)))
- la méthode 'linspace' avec IronPython
Équivalent à
numpy.linspace(0, 10, num=50)
avec numpy
def py_linspace(start, stop, num=50, endpoint=True):
num = int(num)
start = start * 1.
stop = stop * 1.
if num == 1:
yield stop
return
if endpoint:
step = (stop - start) / float(num - 1)
else:
step = (stop - start) / float(num)
for i in range(num):
yield start + step * i
lst = [x for x in py_linspace(1, 10, 22)]
...
import clr
import sys
import System
def get_shape(lst, shape=()):
"""
returns the shape of nested lists
"""
if not hasattr(lst, "__iter__") or isinstance(lst, (str, System.String)):
# base case
return {"shape":shape, "ndim":len(shape)}
# peek ahead and assure all lists in the next depth
# have the same length
if hasattr(lst[0], "__iter__") and not isinstance(lst[0], (str, System.String)):
l = len(lst[0])
if not all(len(item) == l for item in lst):
msg = 'not all lists have the same length'
raise ValueError(msg)
shape += (len(lst), )
# recurse
shape = get_shape(lst[0], shape)
return shape #{"shape":shape, "ndim":len(shape)}
datas_array = IN[0]
OUT = get_shape(datas_array), get_shape(datas_array)['ndim']
- Fusionner des dictionnaires avec Python
# Load the Python Standard and DesignScript Libraries
import sys
import itertools
list_dict = IN[0]
OUT = dict(itertools.chain.from_iterable(dct.items() for dct in list_dict))
# OR
OUT = {}
for d in list_dict: OUT = dict(OUT, **d)
# OR (Python 3.9+)
OUT = {}
for d in list_dict: OUT |= d
- Fusionner des dictionnaires avec Linq
import sys
import clr
import System
from System.Collections.Generic import List, Dictionary
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
list_dict = IN[0]
OUT = list_dict.SelectMany(lambda x : Dictionary[str, object](x))\
.ToDictionary(lambda g : g.Key, lambda g : g.Value)
..
- Retrouver l'index d'un item d'une liste suivant une propriété.
import sys
import clr
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import random
random.seed(282)
lst_pts = [Point.ByCoordinates(*random.sample(range(0, 30), 3)) for i in range(10)]
# get one point from list
pta = random.choice(lst_pts)
# search the index of this point pta
index_pta = next((i for i, pt in enumerate(lst_pts) if pt.IsAlmostEqualTo(pta)), -1)
# OR
func =System.Predicate[Point](lambda pt : pt.IsAlmostEqualTo(pta))
index_pta = List[Point](lst_pts).FindIndex(func)
OUT = lst_pts, pta, index_pta
- Ordonner une liste de points suivant un vecteur
import clr
import sys
import System
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
lstPts = IN[0]
vector = IN[1]
# with Dynamo DesignScript API
sorted_points = sorted(lstPts, key = lambda p : p.AsVector().Dot(vector.Normalized()))
# with Revit API
# sorted_points = sorted(lstPts, key = lambda p : p.DotProduct(vector.Normalize()))
OUT = sorted_points
- Utilisation de la librairie Bisect
Le module bisect de la bibliothèque standard de Python fournit des
fonctions basées sur la recherche binaire (« bissection ») pour :
- Trouver rapidement la position où insérer un élément dans une liste triée tout en gardant l’ordre.
- Insérer un élément dans la bonne position d’une liste triée.
- Rechercher la plage d’indices où un élément donné pourrait se trouver.
Fonction | Description |
---|---|
bisect.bisect_left(a, x)
|
Renvoie l'index où insérer x dans a pour
garder la liste triée (avant les éléments égaux).
|
bisect.bisect_right(a, x) bisect.bisect(a, x)
|
Renvoie l'index où insérer x après les éléments égaux
(position à droite).
|
bisect.insort_left(a, x)
|
Insère x dans a à l'index approprié (à
gauche des égaux).
|
bisect.insort_right(a, x) bisect.insort(a, x)
|
Insère x dans a à droite des égaux.
|
Un exemple avec Dynamo pour remplacer la couleur de l'élément en fonction des intervalles de valeurs des paramètres du projet
.
.
import clr
import sys
import bisect
from bisect import bisect_left, bisect_right
import System
clr.AddReference("System.Numerics")
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
def get_color_by_interval(value, interval_values=[], color_values=[]):
"""
Assigns color based on the input value interval.
Args:
value (float): The input value to assign a color to.
interval_values (List): A list of interval values to determine color assignment.
color_values (List): A list of color values corresponding to the interval values.
Returns:
str: The color assigned to the input value.
"""
i = bisect_right(interval_values, value)
return color_values[i-1]
def assign_colors_to_elements(lst_elems, parameterName, interval_values, color_values):
"""
Assigns colors to elements based on the given parameter value.
Args:
lst_elems (List): List of elements to assign colors to.
parameterName (str): The parameter name to retrieve values from elements.
interval_values (List): A list of interval values to determine color assignment.
color_values (List): A list of color values corresponding to the interval values.
Returns:
List: A list of elements with assigned colors.
"""
out = []
for elem in lst_elems:
para = elem.LookupParameter(parameterName)
if para is not None:
paraValue = float(para.AsValueString())
ds_color = get_color_by_interval(paraValue, interval_values, color_values)
out.append([elem, paraValue, ds_color])
return out
toList = lambda x : UnwrapElement(x) if isinstance(x, list) else UnwrapElement([x])
lst_elems = toList(IN[0])
parameterName = IN[1]
interval_values = toList(IN[2])
color_values = toList(IN[3])
OUT = assign_colors_to_elements(lst_elems, parameterName, interval_values, color_values)
À cet article, je vous recommande de regarder l'article sur l'utilisation
des méthodes d'extension LINQ avec Dynamo PythonNet3
https://voltadynabim.blogspot.com/2025/03/dynamo-python-dspytonnet3-et-linq.html
https://voltadynabim.blogspot.com/2025/03/dynamo-python-dspytonnet3-et-linq.html
0 commentaires:
Enregistrer un commentaire