python初学面向对象

面向对象三要素

1. 封装:
- 组装:将数据和操作组装到一起。
- 隐藏数据:对外只暴露一些借口,通过接口访问对象。比如驾驶员使用汽车,不需要了解汽车的构造细节,只需要知道使用什么部件怎么驾驶就行,踩了油门就能跑,可以不了解后面的机动原理。
2. 继承:
- 多复用,继承来的就不用自己写了
- 多继承少修改,OCP(Open-closed Principle),使用继承来改变,来体现个性。
3. 多态:
- 面向对象编程最灵活的地方,动态绑定
人类就是封装;
人类继承自动物类,孩子继承父母特征。分为单一继承、多继承;
多态、继承自动物类的人类、猫类的操作“吃”不同。

类的定义:

class ClassName:
    语句块
  1. 必须使用class关键字
  2. 类名必须是用驼峰
  3. 类定义完成后,就产生一个类对象,绑定到ClassName上

举例:

class MyClass:
    """"A example class"""
    x = 'abc' #类属性
    def foo(sef): #类属性foo,也是方法
        return 'My Class'
print(MaClass.x)
print(MaClass.foo)
print(MyClass.__doc__)

类属性与类对象

  • 类对象 ,类在定义是就会生成一个类对象。
  • 类的属性 ,类中定义的变量以及类中定义的方法都是类的属性。
  • 类变量 ,类中定义的变量就是类变量,例如上例中的x就是类MyClass的变量。

MyClass中,x、foo都是类的属性,doc也是类属性。
foo方法是类的属性,如同吃是人类的方法,但每一个具体的人才能吃东西,也就是说吃是人的实例才能调用的方法。
foo是method方法对象 ,不是普通的函数对象function了,它必须至少有一个参数,且第一个参数必须是self(self可以换个名字),这个参数位置就留给了self。
self指代当前实例本身

实例化

mycls = MyClass() #实例化,初始化
mycls.foo()
print(mycls.x)

使用上面的语法,在类对象名称后面加一个括号,就调用类的实例化方法,完成实例化。实例化就真正的创建了一个该类的对象(实例)。例如人类的实例tom、jerry。
实例化后获得的实例,是不同的实例,即使是使用相同的参数实例化,也得到不一样的对象。Python类实例化后,会自动调用__int__方法。这个方法第一个参数必须留给self,其他参数随意。

__int__方法

MyClass()实际上调用的是__int__(self)方法,可以不定义,如果没有定义会在实例化后隐式调用。
作用:对实例化进行初始化

class MyClass:
    x=123
    def __init__(self):    #实例化
        print('init')
    def foo(self):
        return 'foo {}'.format(self.x)
a = MyClass()
print(a.foo())

运行结果:

init
foo 123

初始化参数可以有多个参数,请注意第一个位置必须是self,例如init(self,name,age)

class Person:
    def __init__(self,name,age):
        self.name = name
        self.age = age

    def show_age(self):
        return self.age


tom = Person('Tom',23)
jerry = Person('Jerry',24)

print(tom .show_age())
tom.age += 1
print('Now {} is {}'.format(tom.name,tom.age))

print(jerry.show_age())

运行结果:

23
Now Tom is 24
24

注意:__init__()方法不能用返回值,也就是只能是None

实例对象instance

类实例化后一定会获得一个对象,就是实例对象
上例中,a、b就是Person的类实例。
__init__方法的第一个参数self就是指代某一个实例。
类实例化出一个实例对象,实例对象会绑定方法,调用方法时采用jerrt.showage()的方法。
但是定义是showage(self),少传一个参数吗?
这个self就是jerry,Python会把方法的调用者作为第一个参数self的实参传入。
self.name就是jerry对象的那么,name是保存在了jerry对象上,而不是Person类上。所以,称为实例变量

self

class MyClass:
    def __init__(self):
        print('self in init = {}'.format(id(self)))

c = MyClass()
print('c = {}'.format(id(c)))

运行结果:

self in init = 21997776
c = 21997776

上例说明,self就是调用者,就是c对应的实例对象。
self这个名字只是一个惯例,它可以修改,但请不要修改,否则影响代码的可读性
看打印的结果,思考一下执行的顺序,为什么?
–我的理解,在定义实例的时候,就进行了初始化,所以在c = MyClass()的时候,就先去__init__()函数里去执行代码了,所以self in init = 21997776要先执行到,并打印出来。

实例变量和类变量

class Person:
    age = 3
    def __init__(self,name):
        self.name = name

tom = Person('Tom')
jerry = Person('Jerry')

print(tom.name,tom.age)

上例中,age = 3中age是类变量,self.name也是类变量。而tom.name,tom.age是实例变量
实例变量是每一个实例自己的变量,是自己独有的;类变量是类的变量,是类的所有实例共享的属性和方法

特殊属性 含义
__name__ 对象名
__class__ 对象的类型
__dict__ 对象的属性的字典
__qualname__ 类的限定名

举例:

>>> class Person():
        age = 3
        def __init__(self,name):
            self.name = name


>>> 
>>> print(Person.__class__)
<class 'type'>
>>> print(Person.__dict__)
{'__module__': '__main__', 'age': 3, '__init__': <function Person.__init__ at 0x0142FAE0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
>>> tom = Person('Tom')
>>> print(tom.__class__)
<class '__main__.Person'>
>>> 
>>> print(tom.__dict__)
{'name': 'Tom'}
>>> print(tom.__class__.__name__)
Person
>>> print(tom.__class__.__dict__)
{'__module__': '__main__', 'age': 3, '__init__': <function Person.__init__ at 0x0142FAE0>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}

上例中,可以看到类属性保存在类__dict__中,实例属性保存在实例的__dict__中,如果从实例访问类的属性,就需要借助__class__找到所属的类
有了上面只是,再看下面的代码

>>> class Person:
        age = 3
        height = 170
        def __init__(self,name,age=18):
            self.name = name
            self.age = age
>>> tom = Person('Tom')
>>> jerry = Person('Jerry',20)
>>> print(Person.age,tom.age,jerry.age)
3 18 20
>>> Person.age = 50
>>> print(Person.age,tom.age,jerry.age)
50 18 20
>>> print(Person.height,tom.height,jerry.height)
170 170 170
>>> Person.height = 190
>>> print(Person.height,tom.height,jerry.height)
190 190 190
>>> print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
{'__module__': '__main__', 'age': 50, 'height': 190, '__init__': <function Person.__init__ at 0x02614DF8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'Tom', 'age': 18}
{'name': 'Jerry', 'age': 20}
>>> tom.height = 160
>>> print(Person.height,tom.height,jerry.height)
190 160 190
>>> print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
{'__module__': '__main__', 'age': 50, 'height': 190, '__init__': <function Person.__init__ at 0x02614DF8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'Tom', 'age': 18, 'height': 160}
{'name': 'Jerry', 'age': 20}
>>> jerry.height += 20
>>> print(Person.height,tom.height,jerry.height)
190 160 210
>>> print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
{'__module__': '__main__', 'age': 50, 'height': 190, '__init__': <function Person.__init__ at 0x02614DF8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
{'name': 'Tom', 'age': 18, 'height': 160}
{'name': 'Jerry', 'age': 20, 'height': 210}
>>> 
>>> 
>>> Person.weight = 140
>>> print(Person.__dict__,tom.__dict__,jerry.__dict__,sep='\n')
{'__module__': '__main__', 'age': 50, 'height': 190, '__init__': <function Person.__init__ at 0x02614DF8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'weight': 140}
{'name': 'Tom', 'age': 18, 'height': 160}
{'name': 'Jerry', 'age': 20, 'height': 210}
>>> print(tom.weight)
140
>>> print(tom.__dict__['weight'])
Traceback (most recent call last):
  File "<pyshell#92>", line 1, in <module>
    print(tom.__dict__['weight'])
KeyError: 'weight'

总结:
是类的,也是这个类所有实例的,其实都可以访问到;是实例的,只是实例自己的,通过类访问不到。
类变量是属于类的变量,这个类的所有实例可以共享这个变量。
实例可以动态给自己增加一个属性。实例.__dict__[变量名]和实例.变量名都可以访问到。
实例的同名变量会隐藏这类变量,或者说是覆盖这个类变量。
实例属性的查找顺序
指的是实例使用.来访问属性,会先找自己的__dict__,如果没有,然后通过属性__class__找到自己的类,再去类的__dict__中找。
注意,如果实例使用__dict__[变量名]访问变量,将不会按照上面的查找顺序找变量了。
一般来说,类变量使用全大写来命名

装饰一个类

回顾,什么是高阶函数?什么是装饰器函数?
思考,如何装饰一个类?
需求,为一个类通过装饰,增加一些类属性

def addname(name):
    def wrapper(cls):
        cls.NAME = name
        return cls
    return wrapper
'''
#等价于  MyClass = addname('Tom')(MyClass)
#addname是一个带参数的函数,而返回的是wrapper这个函数对象,注意而不是函数的值
#所以addname(''Tom'),相当于是函数wrapper(),addname(''Tom')(MyClass)相当于是wrapper(MyClass),
# 然后此函数又把cls也就是MyClass返回给了MyClass实现了覆盖。
@addname('Tom')
class MyClass:
    pass

print(MyClass.__dict__)
'''

class MyClass:
    pass

MyClass = addname('Tom')(MyClass)
print(MyClass.__dict__)

运行结果:

{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': None, 'NAME': 'Tom'}

类方法和静态方法

前面的例子中定义的__init__等方法,这些方法本身都是类的属性,第一个参数必须是self,而self必须指向一个对象,也就是类必须实例化之后,由实例来调用这个方法。

普通函数

class Person:
    def normal_method(): #这样在类中定义函数可以吗?
        print('naormal')

Person.normal_method()  #这样调用函数可以吗?
Person().normal_method() #这样调用可以吗?

Person.normal_method()可以,因为这个方法只是被Person这个名字空间管理的一个普通方法,normal_method这是Person的一个属性而已。
由于normal_method在定义的时候,没有指定self,所以不能完成实例对象的绑定,不能用Person().normal_method()调用。
注意:虽然语法是对的,但是没有人这么用,也就是说,在类中,禁止写没有self的函数。

类方法

class Person:
   @classmethod
   def class_methon(cls): #cls是什么
       print('class = {0.__name__} {0}'.format(cls))
       cls.LEIGHT = 4

Person.class_methon()
print(Person.__dict__)

类方法

  1. 在类定义中,使用@classmethod装饰器修饰的方法
  2. 必须至少有一个参数,且第一个参数留给cls,cls知道调用者即类对象自身
  3. cls这个标识符可以是任意合法名称,但是为了易读,请不要修改
  4. 通过cls可以直接操作类的属性
    注意:无法通过cls操作类的实例,为什么?
    答:cls是类对象,简言之,类对象在调用这个类方式的时候,类实例,可以不用创建。所以,在没有类实例的情况下,cls还怎么去操作类实例呢?

类方法,类似于C++、Java的静态方法

静态方法

class Person:
    @classmethod
    def class_methon(cls): #cls是什么
       print('class = {0.__name__} {0}'.format(cls))
       cls.LEIGHT = 4

    @staticmethod
    def static_method():
        print(Person.LEIGHT)
Person.class_methon()
Person.static_method()
print(Person.__dict__)

静态方法

  1. 在类的定义中,使用@staticmethod装饰器修饰的方法
  2. 调用时,不会隐式的传入参数
    静态方法,只是表名这个方式属于这个名词空间。函数归在一起,方便组织管理。

方法的调用

class Person:
    def normal():         #类普通函数,类可以直接调用。但禁止在类里这么定义函数
        print('Normal method ')

    def method(self):     #普通方法,只能类的实例调用
         print('method '.format(self.LEIGHT))

    @classmethod          #类方法,类可以直接调用
    def class_methon(cls): #cls是什么
       print('class = {0.__name__} {0}'.format(cls))
       cls.LEIGHT = 4

    @staticmethod         #静态方法,类可以直接调用
    def static_method():
        print(Person.LEIGHT)

print('~~~~类访问~~~~')
print(1,Person.normal()) #可以吗?
#print(2,Person.method())#可以吗
print(3,Person.class_methon())#可以吗
print(4,Person.static_method())#可以吗
print(Person.__dict__)

print('~~~~实例访问~~~~')
print('Tom-------')
tom = Person()
#print(1,tom.normal())#可以吗
print(2,tom.method())#可以吗
print(3,tom.class_methon())#可以吗
print(4,tom.static_method())#可以吗

print(Person.__dict__)

输出结果:

~~~~类访问~~~~
Normal method 
1 None
class = Person <class '__main__.Person'>
3 None
4
4 None
{'__module__': '__main__', 'normal': <function Person.normal at 0x01D8DF60>, 'method': <function Person.method at 0x01D8DF18>, 'class_methon': <classmethod object at 0x003EB470>, 'static_method': <staticmethod object at 0x01D95250>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'LEIGHT': 4}
~~~~实例访问~~~~
Tom-------
method 
2 None
class = Person <class '__main__.Person'>
3 None
4
4 None
{'__module__': '__main__', 'normal': <function Person.normal at 0x01D8DF60>, 'method': <function Person.method at 0x01D8DF18>, 'class_methon': <classmethod object at 0x003EB470>, 'static_method': <staticmethod object at 0x01D95250>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None, 'LEIGHT': 4}

首先理解下输出结果:
print(3,Person.class_methon())#可以吗
输出的是
class = Person

猜你喜欢

转载自blog.csdn.net/feng_vs_sunzhilong/article/details/81428516