include "../_i/1.h"; ?>
def css_generator(selector, **kwargs): """ Returns a formatted CSS block for the given selector. Accepts any number of attribute-value pairs as keyword args. """ return ("%s {\n%s\n}" % (selector, "\n".join(["\t%s: %s;" % (k,v) for k,v in kwargs.items()]))) if __name__ == '__main__': print css_generator('.wildtype', color="#fff")
Save this as, say, css.py
We can run the Python file to execute our test:
$ python css.py .wildtype { color: #fff; }
And we can also import the function into the Python shell for manual tests:
>>> from css import css_generator >>> wildtype_block = css_generator('.wildtype', color="#fff") >>> wildtype_block '.wildtype {\n\tcolor: #fff\n}' >>> print wildtype_block .wildtype { color: #fff; }
Looks great! But there's a pretty serious flaw that a couple of you discovered.
Many CSS attributes
(font-family
, text-size
, etc.) have hyphens
in them. But the hyphen is not a legal character in Python identifiers
(variable names, function names, argument names); it would be
ambiguous with its use for subtraction/negation:
>>> print css_generator('.wildtype', font-weight="bold") File "", line 1 SyntaxError: keyword can't be an expression
Python thinks we're trying to subtract font - weight
,
which would be an expression, not a keyword argument
name.
How could we solve this?
We know that Python identifiers can use underscores; but CSS attributes don't. We'll use underscores in our keyword arguments, and replace them with hyphens in our function.
So we need a way to replace one character with another in a string. Let's see if a string object has any methods that might help:
>>> s = 'some string' >>> dir(s) ['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__str__', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
It has a .replace() method! But how does it work?
>>> help(s.replace) Help on built-in function replace: replace(...) S.replace (old, new[, count]) -> string Return a copy of string S with all occurrences of substring old replaced by new. If the optional argument count is given, only the first count occurrences are replaced.
Try this with one of your own functions: import it and call
help()
on it.
def css_generator(selector, **kwargs): """ Returns a formatted CSS block for the given selector. Accepts any number of attribute-value pairs as keyword args. Use underscores in keyword arg names to represent hyphens in CSS attributes. """ return ("%s {\n%s\n}" % (selector, "\n".join(["\t%s: %s;" % (k.replace('_', '-'),v) for k,v in kwargs.items()]))) if __name__ == '__main__': print css_generator('.wildtype', color="#fff")
>>> print css_generator('.wildtype', font_weight="bold") .wildtype { font-weight: bold; }
def map(func, alist): """ Apply the given function to each element of alist, and return the resulting list. """ return [func(elem) for elem in alist] if __name__ == '__main__': def double(x): return x*2 print map(double, [2, 4, 3])
Brief excursus on functions, scope, references and imports...
include "../_i/3.h" ?>