十五、python class 类学习笔记

来源:互联网 发布:搪瓷 知乎 编辑:程序博客网 时间:2024/05/18 13:08
In [2]:
# problem 1class Foo:    x = 1f1 = Foo()f2 = Foo()print Foo.x, f1.x, f2.xf2.x = 2print Foo.x, f1.x, f2.xFoo.x = 3print Foo.x, f1.x, f2.x
1 1 11 1 23 3 2
In [3]:
# problem 4class A:    x = 1    def f(self):        return self.xclass B(A):    x = 2    def g(self):        return self.xa = A()b = B()print a.f()print b.f(), b.g()
12 2
In [4]:
# problem 7x = 1class Foo:    a = x    x = 2    print a, xprint x
1 21

Example: Timer class

Problem: Write a Timer class.

class Timer:    passdef timepass():    for i in range(10000):        for j in range(1000):        x = i*jt = Timer()t.start()timepass()t.stop()print "took %f seconds" % t.elapsed

Ducktyping

In [10]:
%%file numbers.txtonetwothreefourfive
Writing numbers.txt
In [14]:
def wordcount(fileobj):    lc = len(fileobj.readlines())    fileobj.seek(0)    wc = len(fileobj.read().split())    fileobj.seek(0)    cc = len(fileobj.read())    return lc, wc, ccprint wordcount(open("numbers.txt"))class FakeFile:    def read(self):        return "one\ntwo\nthree\n"    def readlines(self):        return ["one\n", "two\n", "three\n"]    def seek(self, pos):        passprint wordcount(FakeFile())class Foo: passf = Foo()f.read = lambda: ""f.readlines = lambda: []f.seek = lambda n: 0print wordcount(f)    
(5, 5, 23)(3, 3, 14)(0, 0, 0)

Problem: Write a class UpperCaseFile, that takes a fileobj as argument and behaves like a file, but returns everything in uppercase when read.

f = UpperCaseFile(open("numbers.txt"))line = f.readline() # should give "ONE\n"lines = f.readlines() # should give ["TWO\n", "THREE\n", "FOUR\n", "FIVE\n"]f = UpperCaseFile(open("numbers.txt"))print wordcount(f) # should be same as wordcount(open("numbers.txt"))

There is a StringIO class in StringIO module that give file like interface to in-memory object.

In [18]:
from StringIO import StringIOf = StringIO()f.write("hello\nworld\n")f.seek(0)print f.read()f.seek(0)print wordcount(f)
helloworld(2, 2, 12)

Special Class Methods

In [19]:
x = 1print x
1
In [33]:
class Point:    def __init__(self, x, y):        self.x = x        self.y = y            def __str__(self):        return "(%s, %s)" % (self.x, self.y)p = Point(2, 3)print p
(2, 3)
In [27]:
print repr(1)
1
In [28]:
print repr("hello")
'hello'
In [29]:
print "hello"
hello
In [31]:
[1, 2, "3, 4"]
Out[31]:
[1, 2, '3, 4']
In [35]:
print p
(2, 3)
In [36]:
print [p]
[<__main__.Point instance at 0x101fa2f38>]
In [37]:
print repr(p)
<__main__.Point instance at 0x101fa2f38>

We need to implement __repr__ to fix that.

In [41]:
class Point:    def __init__(self, x, y):        self.x = x        self.y = y            def __str__(self):        return "(%s, %s)" % (self.x, self.y)        def __repr__(self):        return "Point(%s, %s)" % (self.x, self.y)p = Point(2, 3)print p, [p, "hello", (2, 3), 5]
(2, 3) [Point(2, 3), 'hello', (2, 3), 5]

Emulating Container Types

In [42]:
# Do you know what happens when you do this?x = [1, 2, 3, 4]print x[1]
2
In [43]:
x.__getitem__(1)
Out[43]:
2
In [44]:
x = xrange(2, 8)print len(x)print x[3]
65
In [45]:
x.__len__()
Out[45]:
6

Example: yrange

Lets write a class yrange that behaves like built-in class xrange.

Example: Node class

In [46]:
class yrange:    def __init__(self, start, stop):        self.start = start        self.stop = stop        def __len__(self):        return self.stop - self.start            def __getitem__(self, index):        return self.start + indexy = yrange(2, 8)print len(y)print y[3]
65

Problem: Implement a Node class that allows accessing properties like a dictionary.

class Node:    def __init__(self, tagname, **attrs):        self.tagname = tagnamex = Node("input", type='text', name='x', id='id_x')x['class'] = 'required'x['value'] = 'foo'print x['name'], x['value']print x
In [48]:
## Command library using classes
In [52]:
%%file command1.pyclass Command:    def run(self, filenames):        lines = self.readfiles(filenames)        lines = self.generate_output(lines)        self.printlines(lines)            def process_line(self, line):        """All bases classes should implement this."""        raise NotImplementedError()    def generate_output(self, lines):        for line in lines:            for outline in self.process_line(line):                yield outline    def readfiles(self, filenames):        for f in filenames:            for line in open(f):                yield line    def printlines(self, lines):        for line in lines:            print line.strip("\n")            
Overwriting command1.py
In [50]:
%%file uppercase1.pyfrom command1 import Commandclass UpperCase(Command):    def process_line(self, line):        yield line.upper()if __name__ == "__main__":    import sys    cmd = UpperCase()    cmd.run(sys.argv[1:])
Writing uppercase1.py
In [53]:
!python uppercase1.py uppercase1.py
FROM COMMAND1 IMPORT COMMANDCLASS UPPERCASE(COMMAND):    DEF PROCESS_LINE(SELF, LINE):        YIELD LINE.UPPER()IF __NAME__ == "__MAIN__":    IMPORT SYS    CMD = UPPERCASE()    CMD.RUN(SYS.ARGV[1:])
In [54]:
%%file grep2.pyfrom command1 import Commandclass Grep(Command):    def __init__(self, pattern):        self.pattern = pattern            def process_line(self, line):        if self.pattern in line:            yield line            if __name__ == "__main__":    import sys    cmd = Grep(sys.argv[1])    cmd.run(sys.argv[2:])
Writing grep2.py
In [55]:
!python grep2.py def grep2.py
    def __init__(self, pattern):    def process_line(self, line):

Problem: Write a script replace.py using Command class to replace a pattern with a replacement in given files. The script will get the pattern and replacement as first two arguments, followed by one or more files as input.

python replace.py def define grep2.py command1.py

Lets try to improve the Command class to handle to make it more generic.

In [56]:
%%file command2.py"""A new approach to writing Command class."""class Command:    def run(self, filenames):        for filename in filenames:            self.process_file(filename)                def process_file(self, filename):        for line in open(filename):            process_line(line)    def process_line(self, line):        """All bases classes should implement this."""        raise NotImplementedError()class UpperCase(Command):    def process_line(self, line):        print line.upper()        class WordCount(Command):    def process_file(self, filename):        lc = len(open(filename).readlines())        wc = len(open(filename).read().split())        cc = len(open(filename).read())        print lc, wc, cc, filename        cmd = WordCount()cmd.run(["command1.py", "command2.py"])
Writing command2.py
In [57]:
!python command2.py
20 47 544 command1.py29 68 808 command2.py

Understanding Methods

In [2]:
class Foo:    x = 1    def getx(self):        return self.x    foo = Foo()print foo.getx()print Foo.getx(foo)
11
In [3]:
Foo.getx
Out[3]:
<unbound method Foo.getx>
In [4]:
foo.getx
Out[4]:
<bound method Foo.getx of <__main__.Foo instance at 0x101f9f3f8>>
In [5]:
def add(x, y):    return x+y
In [7]:
def make_adder(x):    def adder(y):        return add(x, y)    return adderadd5 = make_adder(5)print add5(6)
11
In [8]:
def bindself(method, self):    """Returns a new function with self already bound"""    def f(*a, **kw):        return method(self, *a, **kw)    return ff = bindself(Foo.getx, foo)f()
Out[8]:
1

Looking inside objects

In [10]:
class Foo:    x = 1        def getx(self):        return self.xfoo = Foo()
In [11]:
Foo
Out[11]:
__main__.Foo
In [12]:
dir(Foo)
Out[12]:
['__doc__', '__module__', 'getx', 'x']
In [13]:
Foo.__dict__
Out[13]:
{'__doc__': None, '__module__': '__main__', 'getx': <function __main__.getx>, 'x': 1}
In [14]:
Foo.__dict__['x'] = 2Foo.x
Out[14]:
2
In [15]:
Foo.__dict__['y'] = 3Foo.y
Out[15]:
3
In [17]:
# Lets try to find how add5 is storing value of xdir(add5)
Out[17]:
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']
In [19]:
def inc(x, amount=1): return x+amount
In [20]:
inc.func_defaults
Out[20]:
(1,)

How Python stores variables?

In [21]:
x = 1 # what does this mean?
In [22]:
g = globals()
In [23]:
g['x']
Out[23]:
1
In [24]:
g['x'] = 2
In [25]:
x
Out[25]:
2

Understanding Function Calls

In [28]:
def add(x, y): return x+y
In [29]:
add(5, 4)
Out[29]:
9
In [30]:
d = [1, 2, 3]d[2]
Out[30]:
3
In [31]:
Foo
Out[31]:
__main__.Foo
In [32]:
Foo()
Out[32]:
<__main__.Foo instance at 0x102f49050>
In [34]:
class Adder:    def __init__(self, x):        self.x = x            def __call__(self, y):        return add(self.x, y)        add5 = Adder(5)print add5(6)
11

Problem: Write timeit decorator as a class.

class timeit:    ....@timeitdef timepass():    for i in range(10000):        for j in range(1000):            x = i*j

Understanding Atributes

In [41]:
class Foo(object):    x = 1foo = Foo()
In [42]:
foo.x = 1foo.x
Out[42]:
1
In [46]:
getattr(foo, "x")
Out[46]:
1
In [47]:
class Attr:    def __getattr__(self, name):        return name.upper()
In [48]:
a = Attr()a.x
Out[48]:
'X'
In [49]:
a.y
Out[49]:
'Y'
In [50]:
a.foo
Out[50]:
'FOO'
In [51]:
getattr(a, "x")
Out[51]:
'X'

Old-Style vs. New-Style Classes

In [52]:
x = 1type(x)
Out[52]:
int
In [53]:
type(int)
Out[53]:
type
In [54]:
class Foo: pass
In [55]:
type(Foo)
Out[55]:
classobj
In [56]:
type(file)
Out[56]:
type
In [57]:
class Foo(object): passtype(Foo)
Out[57]:
type
In [58]:
class Foo(object):     def __len__(self): return 4
In [59]:
foo = Foo()len(foo)
Out[59]:
4
In [60]:
foo.__len__ = lambda: 5
In [61]:
len(foo)
Out[61]:
4
In [62]:
class Bar:    def __len__(self): return 4bar = Bar()len(bar)
Out[62]:
4
In [63]:
bar.__len__ = lambda: 5
In [64]:
len(bar)
Out[64]:
5
In [68]:
class Bar:    x = 1    def getx(self): return self.x        bar = Bar()print bar.getx()
1
In [69]:
# What does this mean? bar.getx()# f = bar.getx# f()# What what does x[1]

Properties

In [87]:
class Person(object):    firstname = "Foo"    lastname = "Bar"    _phonenumber = "0"    @property        def fullname(self):        return self.firstname + " " + self.lastname    @property    def phone(self):        return self._phonenumber            @phone.setter    def phone(self, value):        if len(value) != 10:            raise ValueError("Invalid Phone number")        self._phonenumber = value            #phone = property(_get_phone, _set_phone)        p = Person()print p.fullnameprint p.phonep.phone = "1234567890"print p.phone
Foo Bar01234567890
In [88]:
dir(Person)
Out[88]:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_phonenumber', 'firstname', 'fullname', 'lastname', 'phone']
In [89]:
Person.phone
Out[89]:
<property at 0x102f5dcb0>
In [90]:
p.phone
Out[90]:
'1234567890'
In [91]:
p.firstname
Out[91]:
'Foo'
In [92]:
p.__dict__
Out[92]:
{'_phonenumber': '1234567890'}
In [93]:
Person.__dict__
Out[93]:
<dictproxy {'__dict__': <attribute '__dict__' of 'Person' objects>, '__doc__': None, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Person' objects>, '_phonenumber': '0', 'firstname': 'Foo', 'fullname': <property at 0x102f5de10>, 'lastname': 'Bar', 'phone': <property at 0x102f5dcb0>}>
In [95]:
Person.phone.__get__(p)
Out[95]:
'1234567890'

Person.phone is a property object, but p.phone gives us a string value instead of a property.

In [99]:
class SimpleDescriptor(object):    def __get__(self, obj, type=None):        if obj is None:            return self        else:            return 1class Foo:    x = SimpleDescriptor()
In [100]:
Foo.x
Out[100]:
<__main__.SimpleDescriptor at 0x102f5c2d0>
In [101]:
foo = Foo()foo.x
Out[101]:
1

Lets try to implement property class.

In [105]:
class my_property(object):    def __init__(self, getter):        self.getter = getter            def __get__(self, obj, type=None):        if obj is None:            return self        else:            return self.getter(obj)        class Person:    @my_property    def fullname(self):        print "fulname called"        return "Foo Bar"p = Person()print Person.fullnameprint p.fullnameprint p.fullname
<__main__.my_property object at 0x102f67490>fulname calledFoo Barfulname calledFoo Bar
In [111]:
class lazy_property(object):    def __init__(self, getter):        self.getter = getter            def __get__(self, obj, type=None):        if obj is None:            return self        value = self.getter(obj)        obj.__dict__[self.getter.__name__] = value        return value        class Person:    def __init__(self, first, last):        self.first = first        self.last = last            @lazy_property    def fullname(self):        print "fulname called"        return self.first + " " + self.lastp = Person("Foo", "Bar")print Person.fullnameprint p.__dict__print p.fullnameprint p.__dict__print p.fullname        #p = Person("Foo", "Bar2")#print p.fullname
<__main__.lazy_property object at 0x102f7c1d0>{'last': 'Bar', 'first': 'Foo'}fulname calledFoo Bar{'fullname': 'Foo Bar', 'last': 'Bar', 'first': 'Foo'}Foo Bar

__slots__

In [112]:
class Point(object):    def __init__(self, x, y):        self.x = x        self.y = y
In [113]:
p = Point(1, 2)p.__dict__
Out[113]:
{'x': 1, 'y': 2}
In [115]:
class Point2(object):    __slots__ = ["x", "y"]    def __init__(self, x, y):        self.x = x        self.y = yp2 = Point2(1, 2)
In [116]:
p2.__dict__
---------------------------------------------------------------------------AttributeError                            Traceback (most recent call last)<ipython-input-116-6207db46af2a> in <module>()----> 1 p2.__dict__AttributeError: 'Point2' object has no attribute '__dict__'
# Problem1class Foo:    x = 1f1 = Foo()f2 = Foo()print Foo.x, f1.x, f2.xf2.x = 2print Foo.x, f1.x, f2.xFoo.x = 3print Foo.x, f1.x, f2.x# Problem4class A:    x = 1    def f(self):        return self.xclass B(A):    x = 2    def g(self):        return self.xa = A()b = B()print a.f()print b.f(), b.g()# problem7x = 1class Foo():    a = x    x = 2    print a, xprint xdef wordcount(fileobj):    lc = len(fileobj.readlines())    fileobj.seek(0)    wc = len(fileobj.read().split())    fileobj.seek(0)    cc = len(fileobj.read())    return lc, wc, ccprint wordcount(open("numbers.txt"))class FakeFile:    def read(self):        return "one\ntwo\nthree\n"    def readlines(self):        return ["one\n", "two\n", "three\n"]    def seek(self, pos):        passprint wordcount(FakeFile())class Foo:    passf = Foo()f.read = lambda: ""f.readlines = lambda: []f.seek = lambda n: 0from StringIO import StringIOf = StringIO()f.write("hello\nworld\n")f.seek(0)print f.read()f.seek(0)print wordcount(f)class Point:    def __init__(self, x, y):        self.x = x        self.y = y    def __str__(self):        return "(%s,%s)" % (self.x, self.y)    def __repr__(self):        return "Point(%s,%s)" % (self.x, self.y)p = Point(2, 3)print pprint repr(1)print "hello"print repr("hello")print repr(p)x = [1, 2, 3, 4]print x.__getitem__(1)x = xrange(2, 8)print len(x)print x.__len__()class yrange:    def __init__(self, start, stop):        self.start = start        self.stop = stop    def __len__(self):        return self.stop - self.start    def __getitem__(self, index):        return self.start + indexy = yrange(2, 8)print len(y)print y[3]class Command:def run(self,filenames):lines=self.readfiles(filenames)lines=self.generate_output(lines)self.printlines(lines)def process_line(self,line):"""All bases classes should implement this class"""raise NotImplementedError()def generate_output(self,lines):for line in lines:for outline in self.process_line(line):yield outlinedef readfiles(self,filenames):for f in filenames:for line in open(f):yield linedef printlines(self,lines):for line in lines:print line.strip("\n")#over write it nowclass UpperCase(Command):def process_line(self,line):yield line.upper()class Grep(Command):def __init__(self,pattern):self.pattern=patterndef process_line(self,line):if self.pattern in line:yield lineclass Foo:x=1def getx(self):return self.xfoo=Foo()print foo.getx()print Foo.getx(foo)print fooprint dir(Foo)Foo.__dict__['x']=2print Foo.xFoo.__dict__['y']=3print Foo.y#-----Python store the variablesx=1g=globals()print g["x"]g["x"]=2print x#-----understanding function callsdef add(x,y): return x+yprint add(5,4)class Adder:def __init__(self,x):self.x=xdef __call__(self,y):return add(self.x,y)add5=Adder(5)print add5(6)#-----understanding Atributes-----class Foo(object):x=1foo=Foo()foo.x=1print foo.xprint getattr(foo,"x")if __name__=="__main__":import syscmd =UpperCase()cmd.run(["numbers.txt"])class Attr:def __getattr__(self,name):return name.upper()a=Attr()print a.xprint a.yprint a.fooprint getattr(a,"x")#-----new style------x=1print type(x)print type(int)class Foo:passprint type(Foo)type(file)class Foo(object):passprint type(Foo)class Foo(object):def __len__(self):return 4foo =Foo()len(foo)foo.__len__=lambda:5len(foo)class Bar:x=1def __len__(self):return 4def getx(self): return self.xbar =Bar()print len(bar)bar.__len__=lambda:5print len(bar)print bar.getx()f=bar.getxprint f()#-----Properties-----class Person(object):firstname="Foo"lastname="Bar"_phone_number="0"@propertydef fullname(self):return self.firstname+""+self.lastname@property def phone(self):return self._phone_number@phone.setterdef phone(self,value):if len(value)!=10:raise ValueError("Invalid Phone number")self._phone_number=valuep=Person()print p.fullnameprint p.phonep.phone="1234567890"print p.phoneprint dir(Person)print p.__dict__print Person.__dict__class SimpleDecscriptor(object):def __get__(self,obj,type=None):if obj is None:return selfelse:return 1class Foo:x=SimpleDecscriptor()print Foo.xfoo=Foo()print foo.xclass my_property(object):def __init__(self,getter):self.getter=getterdef __get__(self,obj,type=None):if obj is None:return selfelse:return self.getter(obj)class lazy_property(object):def __init__(self,getter):self.getter=getterdef __get__(self,obj,type=None):if obj is None:return selfvalue=self.getter(obj)obj.__dict__[self.getter.__name__]=valuereturn valueclass Person:@lazy_propertydef fullname(self):print "fullname called"return "Foo Bar"p=Person()print Person.fullnameprint p.fullnameprint p.fullnameclass Point(object):def __init__(self,x,y):self.x=xself.y=yp=Point(1,2)print p.__dict__class Point2(object):__slots__=["x",'y']def __init__(self,x,y):self.x=xself.y=yp2 = Point2(1,2)print p2.__dict__

results:
1 1 11 1 23 3 212 21 21(5, 5, 23)(3, 3, 14)helloworld(2, 2, 12)(2,3)1hello'hello'Point(2,3)2666511<__main__.Foo instance at 0x1050e55a8>['__doc__', '__module__', 'getx', 'x']231291111ONETWOTHREEFOURFIVEXYFOOX<type 'int'><type 'type'><type 'classobj'><type 'type'>4511FooBar01234567890['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_phone_number', 'firstname', 'fullname', 'lastname', 'phone']{'_phone_number': '1234567890'}{'phone': <property object at 0x105130158>, '__module__': '__main__', 'firstname': 'Foo', '__dict__': <attribute '__dict__' of 'Person' objects>, 'lastname': 'Bar', 'fullname': <property object at 0x1051300a8>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, '_phone_number': '0'}<__main__.SimpleDecscriptor object at 0x1050ecd10>1<__main__.lazy_property object at 0x1050ece10>fullname calledFoo BarFoo Bar{'y': 2, 'x': 1}Traceback (most recent call last):  File "/Users/macbook/Proj/practise/class.py", line 366, in <module>    print p2.__dict__AttributeError: 'Point2' object has no attribute '__dict__'[Finished in 0.1s with exit code 1]


0 0