|
Python/FAQ/Подпрограммы
Материал из Wiki.crossplatform.ru
Introduction
#-----------------------------
# DO NOT DO THIS...
greeted = 0
def hello():
global greeted
greeted += 1
print "hi there"
#... as using a callable object to save state is cleaner
# class hello
# def __init__(self):
# self.greeted = 0
# def __call__(self):
# self.greeted += 1
# print "hi there"
# hello = hello()
#-----------------------------
hello() # call subroutine hello with no arguments/parameters
#-----------------------------
Accessing Subroutine Arguments
#-----------------------------
import math
# Provided for demonstration purposes only. Use math.hypot() instead.
def hypotenuse(side1, side2):
return math.sqrt(side1**2 + side2**2)
diag = hypotenuse(3, 4) # diag is 5.0
#-----------------------------
print hypotenuse(3, 4) # prints 5.0
a = (3, 4)
print hypotenuse(*a) # prints 5.0
#-----------------------------
both = men + women
#-----------------------------
nums = [1.4, 3.5, 6.7]
# Provided for demonstration purposes only. Use:
# ints = [int(num) for num in nums]
def int_all(nums):
retlist = [] # make new list for return
for n in nums:
retlist.append(int(n))
return retlist
ints = int_all(nums) # nums unchanged
#-----------------------------
nums = [1.4, 3.5, 6.7]
def trunc_em(nums):
for i,elem in enumerate(nums):
nums[i] = int(elem)
trunc_em(nums) # nums now [1,3,6]
#-----------------------------
# By convention, if a method (or function) modifies an object
# in-place, it returns None rather than the modified object.
# None of Python's built-in functions modify in-place; methods
# such as list.sort() are somewhat more common.
mylist = [3,2,1]
mylist = mylist.sort() # incorrect - returns None
mylist = sorted(mylist) # correct - returns sorted copy
mylist.sort() # correct - sorts in-place
#-----------------------------
Making Variables Private to a Function
#-----------------------------
# Using global variables is discouraged - by default variables
# are visible only at and below the scope at which they are declared.
# Global variables modified by a function or method must be declared
# using the "global" keyword if they are modified
def somefunc():
variable = something # variable is invisible outside of somefunc
#-----------------------------
import sys
name, age = sys.args[1:] # assumes two and only two command line parameters
start = fetch_time()
#-----------------------------
a, b = pair
c = fetch_time()
def check_x(x):
y = "whatever"
run_check()
if condition:
print "got", x
#-----------------------------
def save_list(*args):
Global_List.extend(args)
#-----------------------------
Creating Persistent Private Variables
#-----------------------------
## Python allows static nesting of scopes for reading but not writing,
## preferring to use objects. The closest equivalent to:
#{
# my $counter;
# sub next_counter { return ++$counter }
#}
## is:
def next_counter(counter=[0]): # default lists are created once only.
counter[0] += 1
return counter[0]
# As that's a little tricksy (and can't make more than one counter),
# many Pythonistas would prefer either:
def make_counter():
counter = 0
while True:
counter += 1
yield counter
next_counter = make_counter().next
# Or:
class Counter:
def __init__(self):
self.counter = 0
def __call__(self):
self.counter += 1
return self.counter
next_counter = Counter()
#-----------------------------
## A close equivalent of
#BEGIN {
# my $counter = 42;
# sub next_counter { return ++$counter }
# sub prev_counter { return --$counter }
#}
## is to use a list (to save the counter) and closured functions:
def make_counter(start=0):
counter = [start]
def next_counter():
counter[0] += 1
return counter[0]
def prev_counter():
counter[0] -= 1
return counter[0]
return next_counter, prev_counter
next_counter, prev_counter = make_counter()
## A clearer way uses a class:
class Counter:
def __init__(self, start=0):
self.value = start
def next(self):
self.value += 1
return self.value
def prev(self):
self.value -= 1
return self.value
def __int__(self):
return self.value
counter = Counter(42)
next_counter = counter.next
prev_counter = counter.prev
#-----------------------------
Determining Current Function Name
## This sort of code inspection is liable to change as
## Python evolves. There may be cleaner ways to do this.
## This also may not work for code called from functions
## written in C.
#-----------------------------
import sys
this_function = sys._getframe(0).f_code.co_name
#-----------------------------
i = 0 # how far up the call stack to look
module = sys._getframe(i).f_globals["__name__"]
filename = sys._getframe(i).f_code.co_filename
line = sys._getframe(i).f_lineno
subr = sys._getframe(i).f_code.co_name
has_args = bool(sys._getframe(i+1).f_code.co_argcount)
# 'wantarray' is Perl specific
#-----------------------------
me = whoami()
him = whowasi()
def whoami():
sys._getframe(1).f_code.co_name
def whowasi():
sys._getframe(2).f_code.co_name
#-----------------------------
Passing Arrays and Hashes by Reference
#-----------------------------
# Every variable name is a reference to an object, thus nothing special
# needs to be done to pass a list or a dict as a parameter.
list_diff(list1, list2)
#-----------------------------
# Note: if one parameter to zip() is longer it will be truncated
def add_vecpair(x, y):
return [x1+y1 for x1, y1 in zip(x, y)]
a = [1, 2]
b = [5, 8]
print " ".join([str(n) for n in add_vecpair(a, b)])
#=> 6 10
#-----------------------------
# DO NOT DO THIS:
assert isinstance(x, type([])) and isinstance(y, type([])), \
"usage: add_vecpair(list1, list2)"
#-----------------------------
Detecting Return Context
#-----------------------------
# perl return context is not something standard in python...
# but still you can achieve something alike if you really need it
# (but you must really need it badly since you should never use this!!)
#
# see http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/284742 for more
#
# NB: it has been tested under Python 2.3.x and no guarantees can be given
# that it works under any future Python version.
import inspect,dis
def expecting():
"""Return how many values the caller is expecting"""
f = inspect.currentframe().f_back.f_back
bytecode = f.f_code.co_code
i = f.f_lasti
instruction = ord(bytecode[i+3])
if instruction == dis.opmap['UNPACK_SEQUENCE']:
howmany = ord(bytecode[i+4])
return howmany
elif instruction == dis.opmap['POP_TOP']:
return 0
return 1
def cleverfunc():
howmany = expecting()
if howmany == 0:
print "return value discarded"
if howmany == 2:
return 1,2
elif howmany == 3:
return 1,2,3
return 1
cleverfunc()
x = cleverfunc()
print x
x,y = cleverfunc()
print x,y
x,y,z = cleverfunc()
print x,y,z
Passing by Named Parameter
#-----------------------------
thefunc(increment= "20s", start="+5m", finish="+30m")
thefunc(start= "+5m",finish="+30m")
thefunc(finish= "+30m")
thefunc(start="+5m", increment="15s")
#-----------------------------
def thefunc(increment='10s',
finish='0',
start='0'):
if increment.endswith("m"):
pass
#-----------------------------
Skipping Selected Return Values
#-----------------------------
a, _, c = func() # Use _ as a placeholder...
a, ignore, c = func() # ...or assign to an otherwise unused variable
#-----------------------------
Returning More Than One Array or Hash
#-----------------------------
def somefunc():
mylist = []
mydict = {}
# ...
return mylist, mydict
mylist, mydict = somefunc()
#-----------------------------
def fn():
return a, b, c
#-----------------------------
h0, h1, h2 = fn()
tuple_of_dicts = fn() # eg: tuple_of_dicts[2]["keystring"]
r0, r1, r2 = fn() # eg: r2["keystring"]
#-----------------------------
Returning Failure
#-----------------------------
# Note: Exceptions are almost always preferred to error values
return
#-----------------------------
def empty_retval():
return None
def empty_retval():
return # identical to return None
def empty_retval():
pass # None returned by default (empty func needs pass)
#-----------------------------
a = yourfunc()
if a:
pass
#-----------------------------
a = sfunc()
if not a:
raise AssertionError("sfunc failed")
assert sfunc(), "sfunc failed"
#-----------------------------
Prototyping Functions
# Prototypes are inapplicable to Python as Python disallows calling
# functions without using brackets, and user functions are able to
# mimic built-in functions with no special actions required as they
# only flatten lists (and convert dicts to named arguments) if
# explicitly told to do so. Python functions use named parameters
# rather than shifting arguments:
def myfunc(a, b, c=4):
print a, b, c
mylist = [1,2]
mydict1 = {"b": 2, "c": 3}
mydict2 = {"b": 2}
myfunc(1,2,3)
#=> 1 2 3
myfunc(1,2)
#=> 1 2 4
myfunc(*mylist)
#=> 1 2 4
myfunc(5, *mylist)
#=> 5, 1, 2
myfunc(5, **mydict1)
#=> 5, 2, 3
myfunc(5, **mydict2)
#=> 5, 2, 4
myfunc(c=3, b=2, a=1)
#=> 1, 2, 3
myfunc(b=2, a=1)
#=> 1, 2, 4
myfunc(mylist, mydict1)
#=> [1, 2] {'c': 3, 'b': 2} 4
# For demonstration purposes only - don't do this
def mypush(mylist, *vals):
mylist.extend(vals)
mylist = []
mypush(mylist, 1, 2, 3, 4, 5)
print mylist
#=> [1, 2, 3, 4, 5]
Handling Exceptions
#-----------------------------
raise ValueError("some message") # specific exception class
raise Exception("use me rarely") # general exception
raise "don't use me" # string exception (deprecated)
#-----------------------------
# Note that bare excepts are considered bad style. Normally you should
# trap specific exceptions. For instance these bare excepts will
# catch KeyboardInterrupt, SystemExit, and MemoryError as well as
# more common errors. In addition they force you to import sys to
# get the error message.
import warnings, sys
try:
func()
except:
warnings.warn("func raised an exception: " + str(sys.exc_info()[1]))
#-----------------------------
try:
func()
except:
warnings.warn("func blew up: " + str(sys.exc_info()[1]))
#-----------------------------
class MoonPhaseError(Exception):
def __init__(self, phase):
self.phase = phase
class FullMoonError(MoonPhaseError):
def __init__(self):
MoonPhaseError.__init__("full moon")
def func():
raise FullMoonError()
# Ignore only FullMoonError exceptions
try:
func()
except FullMoonError:
pass
#-----------------------------
# Ignore only MoonPhaseError for a full moon
try:
func()
except MoonPhaseError, err:
if err.phase != "full moon":
raise
#-----------------------------
Saving Global Values
# There is no direct equivalent to 'local' in Python, and
# it's impossible to write your own. But then again, even in
# Perl it's considered poor style.
# DON'T DO THIS (You probably shouldn't use global variables anyway):
class Local(object):
def __init__(self, globalname, val):
self.globalname = globalname
self.globalval = globals()[globalname]
globals()[globalname] = val
def __del__(self):
globals()[self.globalname] = self.globalval
foo = 4
def blah():
print foo
def blech():
temp = Local("foo", 6)
blah()
blah()
blech()
blah()
#-----------------------------
Redefining a Function
#-----------------------------
grow = expand
grow() # calls expand()
#-----------------------------
one.var = two.table # make one.var the same as two.table
one.big = two.small # make one.big the same as two.small
#-----------------------------
fred = barney # alias fred to barney
#-----------------------------
s = red("careful here")
print s
#> <FONT COLOR='red'>careful here</FONT>
#-----------------------------
# Note: the 'text' should be HTML escaped if it can contain
# any of the characters '<', '>' or '&'
def red(text):
return "<FONT COLOR='red'>" + text + "</FONT>"
#-----------------------------
def color_font(color, text):
return "<FONT COLOR='%s'>%s</FONT>" % (color, text)
def red(text): return color_font("red", text)
def green(text): return color_font("green", text)
def blue(text): return color_font("blue", text)
def purple(text): return color_font("purple", text)
# etc
#-----------------------------
# This is done in Python by making an object, instead of
# saving state in a local anonymous context.
class ColorFont:
def __init__(self, color):
self.color = color
def __call__(self, text):
return "<FONT COLOR='%s'>%s</FONT>" % (self.color, text)
colors = "red blue green yellow orange purple violet".split(" ")
for name in colors:
globals()[name] = ColorFont(name)
#-----------------------------
# If you really don't want to make a new class, you can
# fake it somewhat by passing in default args.
colors = "red blue green yellow orange purple violet".split(" ")
for name in colors:
def temp(text, color = name):
return "<FONT COLOR='%s'>%s</FONT>" % (color, text)
globals()[name] = temp
#-----------------------------
Trapping Undefined Function Calls with AUTOLOAD
# Python has the ability to derive from ModuleType and add
# new __getattr__ and __setattr__ methods. I don't know the
# expected way to use them to emulate Perl's AUTOLOAD. Instead,
# here's how something similar would be done in Python. This
# uses the ColorFont defined above.
#-----------------------------
class AnyColor:
def __getattr__(self, name):
return ColorFont(name)
colors = AnyColor()
print colors.chartreuse("stuff")
#-----------------------------
## Skipping this translation because 'local' is too Perl
## specific, and there isn't enough context to figure out
## what this is supposed to do.
#{
# local *yellow = \&violet;
# local (*red, *green) = (\&green, \&red);
# print_stuff();
#}
#-----------------------------
Nesting Subroutines
#-----------------------------
def outer(arg1):
x = arg1 + 35
def inner():
return x * 19
return x + inner()
#-----------------------------
Program: Sorting Your Mail
#-----------------------------
import mailbox, sys
mbox = mailbox.PortableUnixMailbox(sys.stdin)
def extract_data(msg, idx):
subject = msg.getheader("Subject", "").strip()
if subject[:3].lower() == "re:":
subject = subject[3:].lstrip()
text = msg.fp.read()
return subject, idx, msg, text
messages = [extract_data(idx, msg) for idx, msg in enumerate(mbox)]
#-----------------------------
# Sorts by subject then by original position in the list
for subject, pos, msg, text in sorted(messages):
print "%s\n%s"%(msg, text)
#-----------------------------
# Sorts by subject then date then original position
def subject_date_position(elem):
return (elem[0], elem[2].getdate("Date"), elem[1])
messages.sort(key=subject_date_position)
# Pre 2.4:
messages = sorted(messages, key=subject_date_position)
#-----------------------------
|