Очевидно, что можно создавать классы во время выполнения или даже новые анонимные типы, но они чрезвычайно ограничены в том, как вы можете использовать их в своем коде.
Если вы предпочитаете работать в современной универсальной среде Queryable
и избегать создания классов во время выполнения, которые имеют ограниченный доступ во время компиляции, вы можете свернуть свой собственный анализатор выражений и построить деревья Expression
. Хитрость заключается в том, чтобы использовать тип Array в качестве возврата из Select
, чтобы сделать члены доступными. Это означает, что все выражения должны возвращать один и тот же тип, но эта реализация при необходимости преобразует все выражения в один тип.
Вот пример реализации:
public static class IQueryableExt {
public static Expression<Func<TRec, TVal?[]>> SelectExpr<TRec, TVal>(this IEnumerable<string> strExprs) where TVal : struct {
var p = Expression.Parameter(typeof(TRec), "p");
var exprs = strExprs.Select(se => {
var e = se.ParseExpression(p);
return e.Type.IsNullableType() && e.Type.GetGenericArguments()[0] == typeof(TVal) ? e : Expression.Convert(e, typeof(TVal?));
}).ToArray();
return Expression.Lambda<Func<TRec, TVal?[]>>(Expression.NewArrayInit(typeof(TVal?), exprs), p);
}
static char[] operators = { '+', '-', '*', '/' };
static Regex tokenRE = new Regex($@"(?=[-+*/()])|(?<=[-+*/()])", RegexOptions.Compiled);
static HashSet<char> hsOperators = operators.ToHashSet();
static Dictionary<char, ExpressionType> opType = new Dictionary<char, ExpressionType>() {
{ '*', ExpressionType.Multiply },
{ '/', ExpressionType.Divide },
{ '+', ExpressionType.Add },
{ '-', ExpressionType.Subtract }
};
static int opPriority(char op) => hsOperators.Contains(op) ? Array.IndexOf(operators, op) >> 1 : (op == ')' ? -1 : -2);
public static Expression ParseExpression(this string expr, ParameterExpression dbParam) {
var opStack = new Stack<char>();
opStack.Push('(');
var operandStack = new Stack<Expression>();
foreach (var t in tokenRE.Split(expr).Where(t => !String.IsNullOrEmpty(t)).Append(")")) {
if (t.Length > 1) // process column name
operandStack.Push(Expression.PropertyOrField(dbParam, t));
else {
while (t[0] != '(' && opPriority(opStack.Peek()) >= opPriority(t[0])) {
var curOp = opStack.Pop();
var right = operandStack.Pop();
var left = operandStack.Pop();
if (right.Type != left.Type) {
if (right.Type.IsNullableType())
left = Expression.Convert(left, right.Type);
else if (left.Type.IsNullableType())
right = Expression.Convert(right, left.Type);
else
throw new Exception($"Incompatible types for operator{curOp}: {left.Type.Name}, {right.Type.Name}");
}
operandStack.Push(Expression.MakeBinary(opType[curOp], left, right));
}
if (t[0] != ')')
opStack.Push(t[0]);
else
opStack.Pop(); // pop (
}
}
return operandStack.Pop();
}
public static bool IsNullableType(this Type nullableType) =>
// instantiated generic type only
nullableType.IsGenericType &&
!nullableType.IsGenericTypeDefinition &&
Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
}
К сожалению, вывод типа не может легко получить тип ответа, поэтому вы должны вручную передать тип записи и тип ответа. Обратите внимание, что в синтаксическом анализаторе есть специальный код для обработки преобразования в (распространенные в SQL) типы, допускающие значения NULL, при смешивании значений Nullable и Nullable.
Учитывая columnsToSelect
, который вы предоставили в качестве примера:
List<string> columnsToSelect = new List<string>();
columnsToSelect.Add("col1 + col2");
columnsToSelect.Add("col2");
columnsToSelect.Add("col3 + col4");
Вы можете запросить базу данных следующим образом:
var queryResult= Context.RawData.Select(columnsToSelect.SelextExpr<TRawData, int>());
И queryResult
будет иметь тип [118 ] или IQueryable<int?[]>
в зависимости от типов столбцов SQL.
Во-первых: Ваши четыре цветовых палитры (черный, зеленый, красный, желтый) не имеют никакого синего компонента. Так, необходимо признать, что выходное изображение едва приблизит входное изображение, если не будет никакого синего компонента для запуска с.
Попробуйте этот код:
import Image
def estimate_color(c, bit, c_error):
c_new= c - c_error
if c_new > 127:
c_bit= bit
c_error= 255 - c_new
else:
c_bit= 0
c_error= -c_new
return c_bit, c_error
def image2cga(im):
"Produce a sequence of CGA pixels from image im"
im_width= im.size[0]
for index, (r, g, b) in enumerate(im.getdata()):
if index % im_width == 0: # start of a line
r_error= g_error= 0
r_bit, r_error= estimate_color(r, 1, r_error)
g_bit, g_error= estimate_color(g, 2, g_error)
yield r_bit|g_bit
def cvt2cga(imgfn):
"Convert an RGB image to (K, R, G, Y) CGA image"
inp_im= Image.open(imgfn) # assume it's RGB
out_im= Image.new("P", inp_im.size, None)
out_im.putpalette( (
0, 0, 0,
255, 0, 0,
0, 255, 0,
255, 255, 0,
) )
out_im.putdata(list(image2cga(inp_im)))
return out_im
if __name__ == "__main__":
import sys, os
for imgfn in sys.argv[1:]:
im= cvt2cga(imgfn)
dirname, filename= os.path.split(imgfn)
name, ext= os.path.splitext(filename)
newpathname= os.path.join(dirname, "cga-%s.png" % name)
im.save(newpathname)
Это создает изображение палитры PNG только с первыми четырьмя наборами записей палитры к Вашим цветам. Это демонстрационное изображение:
становится
Это тривиально для взятия вывода image2cga
(приводит к последовательности 0-3 значений), и упакуйте каждые четыре значения к байту.
Если Вы нуждаетесь в помощи о том, что делает код, спросите, и я объясню.
Конечно, оказывается, что я был слишком восторжен и — как обнаруженный Thomas — метод Image.quantize может взять изображение палитры в качестве аргумента и сделать квантование с намного лучшими результатами, чем мой специальный метод выше:
def cga_quantize(image):
pal_image= Image.new("P", (1,1))
pal_image.putpalette( (0,0,0, 0,255,0, 255,0,0, 255,255,0) + (0,0,0)*252)
return image.convert("RGB").quantize(palette=pal_image)
Для "добавленной стоимости", здесь следует коду для создания упакованной строки (4 пикселя за байт):
import itertools as it
# setup: create a map with tuples [(0,0,0,0)‥(3,3,3,3)] as keys
# and values [chr(0)‥chr(255)], because PIL does not yet support
# 4 colour palette images
TUPLE2CHAR= {}
# Assume (b7, b6) are pixel0, (b5, b4) are pixel1…
# Call it "big endian"
KEY_BUILDER= [
(0, 64, 128, 192), # pixel0 value used as index
(0, 16, 32, 48), # pixel1
(0, 4, 8, 12), # pixel2
(0, 1, 2, 3), # pixel3
]
# For "little endian", uncomment the following line
## KEY_BUILDER.reverse()
# python2.6 has itertools.product, but for compatibility purposes
# let's do it verbosely:
for ix0, px0 in enumerate(KEY_BUILDER[0]):
for ix1, px1 in enumerate(KEY_BUILDER[1]):
for ix2, px2 in enumerate(KEY_BUILDER[2]):
for ix3, px3 in enumerate(KEY_BUILDER[3]):
TUPLE2CHAR[ix0,ix1,ix2,ix3]= chr(px0+px1+px2+px3)
# Another helper function, copied almost verbatim from itertools docs
def grouper(n, iterable, padvalue=None):
"grouper(3, 'abcdefg', 'x') --> ('a','b','c'), ('d','e','f'), ('g','x','x')"
return it.izip(*[it.chain(iterable, it.repeat(padvalue, n-1))]*n)
# now the functions
def seq2str(seq):
"""Takes a sequence of [0..3] values and packs them into bytes
using two bits per value"""
return ''.join(
TUPLE2CHAR[four_pixel]
for four_pixel in grouper(4, seq, 0))
# and the image related function
# Note that the following function is correct,
# but is not useful for Windows 16 colour bitmaps,
# which start at the *bottom* row…
def image2str(img):
return seq2str(img.getdata())
John, я нашел, что сначала связываются также, но это непосредственно не помогло мне с проблемой. Это действительно заставляло меня выглядеть глубже, квантуют все же.
Я придумал это вчера перед тем, чтобы ложиться спать:
import sys
import PIL
import Image
PALETTE = [
0, 0, 0, # black, 00
0, 255, 0, # green, 01
255, 0, 0, # red, 10
255, 255, 0, # yellow, 11
] + [0, ] * 252 * 3
# a palette image to use for quant
pimage = Image.new("P", (1, 1), 0)
pimage.putpalette(PALETTE)
# open the source image
image = Image.open(sys.argv[1])
image = image.convert("RGB")
# quantize it using our palette image
imagep = image.quantize(palette=pimage)
# save
imagep.save('/tmp/cga.png')
TZ.TZIOY, Ваше решение, кажется, работает вдоль тех же принципов. Престижность, я должен был прекратить работать над ним и ожидать Вашего ответа. Мой немного более прост, хотя определенно не более логичный, чем Ваш. PIL является громоздким для использования. Ваш объясняет, что продолжает делать это.