Can the idea of LINQ in C # be useful in Maya Python? I tried to find out.
――I want to operate the list more easily and easily. --If you can write something similar to C #, the switching cost should go down.
I will leave the explanation about LINQ to other sites (reference sites are summarized at the bottom of the page) Roughly speaking, it is "* a function to filter and process values in a SQL statement-like way for lists etc. *". The code that actually uses LINQ is as follows.
LinqSample.cs
var hoge = new int[] { 0, 1, 2, 3, 4, 5 };
var hogehoge = hoge.where(n => n > 3).select(n => n * n);
foreach (var x in hogehoge)
Debug.WriteLine(x);
>>> 16
>>> 25
If you write the above code in Python's ** list comprehension **, it will be as follows.
list_comprehension_sample.py
hoge = [0, 1, 2, 3, 4, 5]
hogehoge = [x * x for x in hoge if x > 3]
for x in hogehoge:
print x
>>> 16
>>> 25
If you write it like this, I think it would be nice if there was a list comprehension notation. (Sasuga Python) Furthermore, since slices can be used in Python, there are few problems with list operations.
LINQ features such as ** method chain ** and ** lazy evaluation **, I think the big advantage is that it is easy to understand and write while simply shortening the code. Writing complex operations with Python list comprehensions and slices can significantly reduce readability.
Based on this situation, I thought about how to reproduce the good parts of LINQ.
First of all, I tried to make only the interface like that to check the atmosphere. The first trial is to prepare the same method as LINQ and bring only the single effect closer.
iterator_v1.py
class GeneralIterator(object):
"""A class that wraps list operations using LINQ method names
<usage>
selection = GeneralIterator(cmds.ls(sl=True))
selection.count()
for x in selection.generator(): print x
selection.last()
selection.first()
selection.at(3)
selection.distinct()
selection.skip(3)
selection.take(3)
selection.all(lambda x: x.startswith('mesh_'))
selection.any(lambda x: x.startswith('skel_'))
selection.contains("grp_")
selection.union(["group1", "group2"])
selection.reverse()
selection.select(lambda x: cmds.getAttr(x + '.tx'))
selection.where(lambda x: x.endswith('_offset'))
"""
def __init__(self, list=None):
self.set_list(list)
def set_list(self, list):
self.__list = list
def is_empty(self):
return self.__list is None or len(self.__list) == 0
def print_items(self):
for x in self.generator():
print x
def count(self):
if self.is_empty():
return 0
return len(self.__list)
def generator(self):
for x in self.__list:
yield x
def first(self, default=None):
if self.is_empty():
return default
return self.__list[0]
def last(self, default=None):
if self.is_empty():
return default
return self.__list[-1]
def at(self, index, default=None):
if index <= self.count():
return self.__list[index]
return default
def distinct(self):
return list(set(self.__list))
def skip(self, count):
if count < self.count():
return self.__list[count:]
def take(self, count):
if count <= self.count():
return self.__list[:count]
def all(self, func):
for x in self.generator():
if not func(x):
return False
return True
def any(self, func):
for x in self.generator():
if func(x):
return True
return False
def contains(self, obj):
for x in self.generator():
if x == obj:
return True
return False
def union(self, list):
return self.__list + list
def reverse(self):
return list(reversed(self.__list))
def select(self, func):
return [func(x) for x in self.__list]
def where(self, func):
return [x for x in self.__list if func(x)]
As a next step, I want a method chain. I would like to see delayed execution. After reading the implementation method of LINQ, I thought that I could make something similar by using closures, and I tried the second one.
iterator_v2.py
class EnumerableIterator(object):
"""List manipulation class that tried method chain and lazy execution like LINQ
[usage]
hoge = EnumerableIterator(range(10))
for x in hoge.where(lambda x: x > 7).select(lambda x: x * x): print x
"""
def __init__(self, list=None, func=None):
self._set_list(list)
self.func = func
def _set_list(self, list):
self.__list = list
def __execute_func(self):
if self.func is None:
return self.__list
return self.func(self.__list)
def __iter__(self):
for x in self.__execute_func():
yield x
def to_list(self):
return self.__execute_func()
def count(self):
return len(self.__execute_func())
def __is_empty(self, list):
return list is None or len(list) == 0
def first(self, default=None):
result = self.__execute_func()
if self.__is_empty(result):
return default
return result[0]
def last(self, default=None):
result = self.__execute_func()
if self.__is_empty(result):
return default
return result[-1]
def at(self, index, default=None):
result = self.__execute_func()
if self.__is_empty(result):
return default
if index <= len(result):
return list[index]
return default
def distinct(self):
return list(set(self.__execute_func()))
def skip(self, count):
result = self.__execute_func()
return result[count:]
def take(self, count):
result = self.__execute_func()
return result[:count]
def all(self, func):
for x in self:
if not func(x):
return False
return True
def any(self, func):
for x in self:
if func(x):
return True
return False
def contains(self, obj):
for x in self:
if x == obj:
return True
return False
def union(self, list):
return self.__execute_func() + list
def reverse(self):
return list(reversed(self.__execute_func()))
def where(self, func):
def action(list):
result = list
if self.func is not None:
result = self.func(list)
return [x for x in result if func(x)]
return EnumerableIterator(self.__list, action)
def select(self, func):
def action(list):
result = list
if self.func is not None:
result = self.func(list)
return [func(x) for x in result]
return EnumerableIterator(self.__list, action)
If nothing is done, the content will not be related to Maya, so I will try using it in Maya.
iterator_v2.py
class EnumerableSelection(EnumerableIterator):
"""Object selection iterator
[usage]
selection = Selection()
for x in selection.where(lambda x: x.endswith('_offset')).select(lambda x: cmds.getAttr(x + '.tx')):
print x
print selection \
.where(lambda x: x.endswith('Group')) \
.select(lambda x: cmds.getAttr(x + '.tx')) \
.where(lambda x: x > 0.1) \
.first()
"""
def __init__(self, flat=True):
super(EnumerableSelection, self).__init__()
self.__flat = flat
self.update()
def update(self):
self._set_list(cmds.ls(sl=True, fl=self.__flat))
There are also itertools and more-itertools, but I prefer LINQ as the writing style. I feel that if I can understand and use the language specifications more, I will be able to write in Pythonic style. I would like to continue studying while waiting for advice and advice from various fields.
-I made a list of LINQ extension methods and almost all the samples. --Go to the horizon -LINQ mechanism & correct basic knowledge of lazy evaluation- @ IT
Recommended Posts