python核心编程:学习笔记5--面向对象编程

1. 类的概述

1. 类与实例

    类与实例相互关联着:类是对象的定义,而实例是"真正的实物",它存放了类中所定义的对象的具体信息.

class MyNewObjectType(bases):
    "define MyNewObjectType class"
    class_suite
    而object是"所有类之母".我们可以给类添加属性
>>> class MyData(object):
	pass

>>> mathObj = MyData()
>>> mathObj.x = 4
>>> mathObj.y = 5
>>> mathObj.x * mathObj.y
20
>>> dir(mathObj)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'x', 'y']

2. 方法

    我们可以给类添加功能(方法),调用方法的途径如下:(1)定义类(和方法),(2)创建一个实例;(3)用这个实例调用方法:

>>> class MyDataWithMethod(object):
	def printFoo(self):
		print("you invoked printFoo()!")

		
>>> myObj = MyDataWithMethod()
>>> myObj.printFoo()
you invoked printFoo()!
    这里self类似于C++的this,而python中的方法调用类中的其他变量或函数时,需要通过self来引用:
>>> class TestClass(object):
	def printFunc(self):
		print(self.show())
	def errorPrintFunc(self):
		print(show())
	def show(self):
		return "hello"

	
>>> test = TestClass()
>>> test.printFunc()
hello
>>> test.errorPrintFunc()
Traceback (most recent call last):
  File "<pyshell#30>", line 1, in <module>
    test.errorPrintFunc()
  File "<pyshell#27>", line 5, in errorPrintFunc
    print(show())
NameError: name 'show' is not defined

3. 创建一个类(类定义)

>>> class AddrBookEntry(object):
	"""address book entry class"""
	def __init__(self, nm, ph):
		self.name = nm
		self.phone = ph
		print("created instance for: %s" % self.name)
	def updatePhone(self, newph):
		self.phone = newph;
		print("updated phone# for:%s" % self.name)
__init__():在实例化时被调用,关联C++的构造函数即可.

4. 创建实例并进行操作

>>> john = AddrBookEntry("john doe", "123-456-789")
created instance for: john doe
>>> jane = AddrBookEntry("hane doe", "987-654-321")
created instance for: hane doe
>>> john
<__main__.AddrBookEntry object at 0x000000000333D668>
>>> john.name
'john doe'
>>> jane.phone
'987-654-321'
>>> jane.updatePhone("111-222-333")
updated phone# for:hane doe
>>> jane.phone
'111-222-333'

5. 创建/使用子类

>>> class EmplAddrBookEntry(AddrBookEntry):
	"""Employee Address Book Entry class"""
	def __init__(self, nm, ph, id, em):
		AddrBookEntry.__init__(self, nm, ph)
		self.empid = id
		self.email = em
	def updateEmail(self, newem):
		self.email = newem
		print("updated e-mail address for: %s" % self.name)
>>> john = EmplAddrBookEntry("john doe", "123-456-789", 42, "[email protected]")
created instance for: john doe
>>> john.email
'[email protected]'
>>> john.updateEmail("[email protected]")
updated e-mail address for: john doe
>>> john.email
'[email protected]'

2. 类和类的属性

    属性就是属于另一个对象的数据或者函数元素,可以通过句点属性标识法来访问.

    类属性仅与被定义的类相绑定,它与具体的实例无关.

    类的数据属性表示这些数据与类对象绑定,不依赖于任何类实例:

>>> class C(object):
	foo = 100

	
>>> print(C.foo)
100
>>> C.bar = 200
>>> print(C.bar)
200

1. 方法

    方法是一个作为类定义一部分定义的函数,必须通过类的实例来调用:即方法必须绑定到一个实例才能直接调用.

>>> class MyClass(object):
	def myMethod(self):
		pass

	
>>> mc = MyClass()
>>> mc.myMethod()
>>> MyClass.myMethod()
Traceback (most recent call last):
  File "<pyshell#75>", line 1, in <module>
    MyClass.myMethod()
TypeError: myMethod() missing 1 required positional argument: 'self'
    而我们通常可以通过dir()和属性__dict__来查看类的属性:
>>> dir(MyClass)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'myMethod']
>>> MyClass.__dict__
mappingproxy({'__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__doc__': None, '__module__': '__main__', 'myMethod': <function MyClass.myMethod at 0x000000000333EA60>})

2. 特殊的类属性

C.__name__
类C的名字
C.__doc__
类C的文档字符串
C.__bases__
类C的所有父类构成的元组
C.__dict__
类C的属性
C.__module
类C定义所在的模块
C.__class__
实例C所对应的类

3. 实例和实例属性

1. 实例

1. 初始化:通过调用类对象来创建实例

>>> class MyClass(object):
	pass

>>> mc = MyClass()
>>> type(mc)
<class '__main__.MyClass'>
>>> type(MyClass)
<class 'type'>

2. __init__()"构造器"方法

    当类被调用,实例化的第一步是创建实例对象.一旦对象创建了,python检查是否实现了__init__()方法.__init__()类似构造函数,任何需要的特定操作都要在__init__()中完成,例如定义类的数据变量.如果__init__()已经被实现,那么它将被调用,实例对象作为第一个参数(self)被传递进去.

3. __new__()"构造器"方法

    对内建类型进行派生的情况下,解释器需要调用类的__new__()方法,一个静态方法,并且传入的参数是在类实例中操作时生成的.

4. __del__()"解构器"方法

    __del__()要直到该实例对象所有的引用都被清除后才会执行,通常在python中不实现,因为实例很少被显式释放.

class P(object):
    def __del__(self):
        pass

class C(P):
    def __init__(self):
        super(C, self).__init__()
        print("initialized")
    def __del__(self):
        super(C, self).__del__()
        print("deleted")

if __name__ == "__main__":
    c1 = C()
    c2 = c1
    c3 = c1
    print([id(x) for x in (c1, c2, c3)])
    del c1
    del c2
    del c3
输出如下:
>>> 
initialized
[67873480, 67873480, 67873480]
deleted

2. 实例属性

1. "实例化"实例属性(或创建一个更好的构造器)

    设置实例的属性可以在实例创建后任意时间进行,也可以在能够访问实例的代码中进行.构造器__init__()是设置这些属性的关键点之一.

我们可以在__init__函数中设置实例属性,并且可以提供默认的参数,且__init__应当返回None:

2. 查看实例属性

>>> class C(object):
	pass

>>> c = C()
>>> c.foo = "roger"
>>> c.bar = "shrubber"
>>> dir(c)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']
>>> c.__dict__
{'bar': 'shrubber', 'foo': 'roger'}
>>> c.__class__
<class '__main__.C'>

3. 实例属性VS类属性

    类属性与实例无关,类属性可以理解为静态成员.类是类属性的名字空间,而实例是实例属性的名字空间.

实例无法更改类的不可变属性:

>>> class MyClass(object):
	version = 1.2
	print("version id:%d" %id(version))

	
version id:48859632
>>> c = MyClass()
>>> c.version
1.2
>>> id(c.version)
48859632
>>> MyClass.version += 0.1
>>> id(MyClass.version)
48858456
>>> c.version
1.3
>>> id(c.version)
48858456
>>> c.version += 0.4
>>> id(c.version)
48859632
>>> c.version
1.7000000000000002
>>> id(MyClass.version)
48858456

    这里之所以是新建一个实例属性version,是因为version不可改变.如果类属性为可改变的,比如列表,则情况完全不同

>>> class MyClass(object):
	version = [1, 2]
	print("version id:%d" %id(version))

	
version id:57720328
>>> c = MyClass()
>>> c.version.append(3)
>>> id(c.version)
57720328
>>> id(MyClass.version)
57720328


3. 绑定和方法调用

    首先,方法仅仅是类内部定义的函数(这意味着方法是类属性而不是实例属性).

    其次,方法只有在其所属的类拥有实例时,才能被调用.当存在一个实例时,方法才被认为是绑定到那个实例了.没有实例时方法就是未绑定的.

    最后,任何一个方法定义中的第一个参数都是变量self,它表示调用此方法的实例对象.

    一般情况下,方法肯定用到了self,来操作类的变量(self.name,...).如果方法中没有使用实例self,则通常定义方法为常规函数:

>>> class MyClass(object):
	def __init__(self):
		self.name = "lcj"
	def show(self):
		print(self.name)
	def noBingShow():
		print("hello world")

		
>>> c = MyClass()
>>> c.show()
lcj
>>> c.noBingShow()
Traceback (most recent call last):
  File "<pyshell#167>", line 1, in <module>
    c.noBingShow()
TypeError: noBingShow() takes 0 positional arguments but 1 was given
>>> MyClass.noBingShow()
hello world
    而非绑定方法通常用于:在子类构造器中调用父类的构造器并且明确的传递(父类)构造器所需要的self参数.这时候需要通过父类名来调用它:
>>> class EmplAddrBookEntry(AddrBookEntry):
	"""Employee Address Book Entry class"""
	def __init__(self, nm, ph, id, em):
		AddrBookEntry.__init__(self, nm, ph)
		self.empid = id
		self.email = em

4. 静态方法和类方法

    可以使用装饰器来编写静态方法和类方法:


class TestStaticMethod(object):
    @staticmethod
    def foo():
        print("calling static method foo()!")

class TestClassMethod(object):
    @classmethod
    def foo(cls):
        print("calling class method foo()")
        print("foo() is part of class:%s" % cls.__name__)
        

		
>>> tsm = TestStaticMethod()
>>> tsm.foo()
calling static method foo()
>>> TestStaticMethod.foo()
calling static method foo()
>>> tcm = TestClassMethod()
>>> tcm.foo()
calling class method foo()
foo() is part of class:TestClassMethod
>>> TestClassMethod.foo()
calling class method foo()
foo() is part of class:TestClassMethod



    而静态方法和类方法的区别在stackoverflow上已说明:

http://stackoverflow.com/questions/136097/what-is-the-difference-between-staticmethod-and-classmethod-in-python

5. 继承

    继承描述了基类的属性如何"遗传"给派生类.一个子类可以继承它的基类的任何属性,不管是数据属性还是方法.

>>> class P(object):
	"""P class"""
	def __init__(self):
		print("created an instance of %s" % self.__class__.__name__)

		
>>> class C(P):
	pass

>>> p = P()
created an instance of P
>>> p.__class__
<class '__main__.P'>
>>> P.__bases__
(<class 'object'>,)
>>> P.__doc__
'P class'
>>> c = C()
created an instance of C
>>> c.__class__
<class '__main__.C'>
>>> C.__bases__
(<class '__main__.P'>,)
>>> C.__doc__

1. __bases__类属性

    对任何子类,__bases__是一个包含其父类的集合的元组:

>>> class A(object): pass

>>> class B(A): pass

>>> class C(B): pass

>>> A.__bases__
(<class 'object'>,)
>>> B.__bases__
(<class '__main__.A'>,)
>>> C.__bases__
(<class '__main__.B'>,)

2. 通过继承覆盖方法

>>> class P(object):
	def foo(self):
		print("Hi, I am P-foo()")

		
>>> class C(P):
	def foo(self):
		print("Hi, I am C-foo()")

		
>>> c = C()
>>> c.foo()
Hi, I am C-foo()
    这时,如果我想调用父类的foo()方法,应该将实例c传入类P中:
>>> P.foo(c)
Hi, I am P-foo()
    而更一般的情况是:我们通过调用super内建方法:
>>> class C(P):
	def foo(self):
		super(C, self).foo()
		print("Hi, I am C-foo()")

		
>>> c = C()
>>> c.foo()
Hi, I am P-foo()
Hi, I am C-foo()
核心笔记:重写__init__不会自动调用基类的__init__

    当从一个带构造器__init__()的类派生,如果你不去覆盖__init__(),它将会被继承并自动调用.但如果你在子类中覆盖了__init__(),子类被实例化时,基类的__init__()就不会被自动调用:

>>> class P(object):
	def __init__(self):
		print("calling P's constructor")

		
>>> class C(P):
	def __init__(self):
		print("calling C's constructor")

		
>>> c = C()
calling C's constructor
    而我们可以通过super来达到目的:super的好处是,我们不需要明确给出任何基类名字:
>>> class P(object):
	def __init__(self):
		print("calling P's constructor")

		
>>> class C(P):
	def __init__(self):
		super(C, self).__init__()
		print("calling C's constructor")

		
>>> c = C()
calling P's constructor
calling C's constructor

3. 从标准类型派生

1. 不可变类型的例子

>>> class RoundFloat(float):
	def __new__(cls, val):
		return super(RoundFloat, cls).__new__(cls, round(val, 2))

	
>>> RoundFloat(1.5955)
1.6
>>> RoundFloat(1.5945)
1.59
>>> RoundFloat(-1.9955)
-2.0

2. 可变类型的例子

>>> class SortedKeyDict(dict):
	def keys(self):
		return sorted(super(SortedKeyDict, self).keys())

	
>>> d = SortedKeyDict((("x" , 1), ("z" , 2), ("y", 3)))
>>> [key for key in d]
['y', 'x', 'z']
>>> d.keys()
['x', 'y', 'z']

4. 多重继承--仅讨论新式类

    一个实例如下:

class P1(object):
    def foo(self):
        print("called P1-foo()")

class P2(object):
    def foo(self):
        print("called P2-foo()")
    def bar(self):
        print("called P2-bar()")

class C1(P1, P2):
    pass

class C2(P1, P2):
    def bar(self):
        print("called C2-bar()")

class GC(C1, C2):
    pass
    这里的多重继承,采取的查询方法为广度优先搜索:即对于GC来说,现搜索C1,C2.而旧类为深度搜索:现搜索C1,P1,然后搜索C2,P2:
>>> gc = GC()
>>> gc.foo()
called P1-foo()
>>> gc.bar()
called C2-bar()

6. 类,实例和其他对象的内建函数

issubclass():

    判断一个类是另一个类的子类或子孙类:

issubclass(sub, sup)
isinstance():

    判定一个对象是否是另一个给定类的实例:

isinstance(obj1, obj2)
obj1是类obj2的一个实例,或者是obj2的子类的一个实例时,返回True.

hasattr(),getattr(),setattr(),delattr():

    *attr()系列函数有两个参数,第一个参数是传递进来的方法,第二个参数为字符串属性名.

    hasattr()函数是布尔型,决定一个对象是否有一个特定的属性,一般用于访问某属性前先作一个检查.getattr()和setattr()函数相应的取得和赋值给对象的属性,getattr()读取不存在的属性时,引发AttributeError异常,除非给出那个可选的默认参数.setattr()将要么加入一个新的属性,要么取代一个已存在的属性.而delattr()函数会从一个对象中删除属性.

>>> class myClass(object):
	def __init__(self):
		self.foo = 100

		
>>> myInst = myClass()
>>> hasattr(myInst, "foo")
True
>>> getattr(myInst, "foo")
100
>>> hasattr(myInst, "bar")
False
>>> getattr(myInst, "bar")
Traceback (most recent call last):
  File "<pyshell#288>", line 1, in <module>
    getattr(myInst, "bar")
AttributeError: 'myClass' object has no attribute 'bar'
>>> setattr(myInst, "bar", "my attr")
>>> dir(myInst)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo']
>>> getattr(myInst, "bar")
'my attr'
>>> delattr(myInst, "foo")
>>> dir(myInst)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar']
dir():

    显示模块的所有属性信息.

super():

    子类用于查找父类的属性.

vars():

    与dir()相似,只是给定的对象参数都必须有一个__dict__属性.


你可能感兴趣的:(python核心编程:学习笔记5--面向对象编程)