13--Python 面向对象基础

@Author : Roger TX ([email protected])
@Link : https://github.com/paotong999

一、面向对象编程思想

面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想。OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
在Python中,所有数据类型都可以视为对象,当然也可以自定义对象。自定义的对象数据类型就是面向对象中的类(Class)的概念。

二、类与对象(实例)

在面向对象的程序设计过程中有两个重要概念:类(class)和对象(object,也被称为实例,instance)

  • 类是某一批对象的抽象,可以把类理解成某种概念
  • 对象是根据类创建出来的一个个具体的“对象”,一个具体存在的实体
类与对象

Python定义类的简单语法如下:

class 类名(object):
    执行语句...
    零个到多个类变量...
    零个到多个方法...

Python 的语法要求:如果从程序的可读性方面来看,Python 的类名必须是由一个或多个有意义的单词连缀而成的,每个单词首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符。

需要说明的是:

  • 类中各成员之间的定义顺序没有任何影响,各成员之间可以相互调用。
  • (object) 表示该类是从哪个类继承下来的,继承的概念我们后面再讲,通常,如果没有合适的继承类,就使用object类,这是所有类最终都会继承的类。

对象(实例)的创建和使用:

class Person :
    '这是一个学习Python定义的一个Person类'
    # 下面定义了一个类变量
    hair = 'black'
    def __init__(self, name = 'Charlie', age=8):
        # 下面为Person对象增加2个实例变量
        self.name = name
        self.age = age

    def say(self, content):
        # 下面定义了一个say方法
        print(content)
        print(self.age)

f = Person()
f.__init__(age=11)    # 等同于Person(age=11)
f.say('123')
  • 创建实例可以通过类名+() Person() 实现
  • 创建对象时默认调用构造方法,可以传入参数,初始化实例变量
  • __init__构造函数只能 return None

Python 类所包含的最重要的两个成员就是变量和方法:

  • 类变量属于类本身,用于定义该类本身所包含的状态数据
  • 实例变量则属于该类的实例对象,用于定义实例对象所包含的状态数据
  • 方法则用于定义该类的对象的行为或功能实现

在类中定义的方法默认是实例方法,实例方法至少应该定义一个参数,该参数通常会被命名为 self。

  • 定义实例方法的方法与定义函数的方法基本相同,只是实例方法的第一个参数会被绑定到方法的调用者(该类的实例)。
  • 类所包含的类变量可以动态增加或删除(程序在类体中为新变量赋值就是增加类变量),程序也可在任何地方为已有的类增加变量;程序可通过 del 语句删除己有类的类变量。
  • 实例对象的实例变量也可以动态增加或删除(只要对新实例变量赋值就是增加实例变量),因此程序可以在任何地方为己有的对象增加实例变量;程序可通过 del 语句删除已有对象的实例变量。

使用 __slots__ 限制实例的属性
Python允许在定义class的时候,定义一个特殊的 __slots__ 变量,来限制该class实例能添加的属性

class Student(object):
    __slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
  • 由于'score'没有被放到 __slots__ 中,所以不能绑定score属性,试图绑定score将得到AttributeError的错误
  • 使用 __slots__ 要注意,__slots__ 定义的属性仅对当前类实例起作用,对继承的子类是不起作用的

三、变量与方法

构造方法

  1. 在实例方法中有一个特别的方法:__init__,这个方法被称为构造方法。构造方法用于构造该类的对象,Python 通过调用构造方法返回该类的对象(无须使用 new)。

  2. 构造方法是一个类创建对象的根本途径,因此 Python 还提供了一个功能:如果开发者没有为该类定义任何构造方法,那么 Python 会自动为该类定义一个只包含一个 self 参数的默认的构造方法。

方法的调用

  1. 对象访问方法或变量的语法是: 对象.变量|方法(参数)。在这种方式中,对象是主调者,用于访问该对象的变量或方法。

  2. 在使用类调用实例方法时,Python 不会自动为第一个参数绑定调用者。实际上也没法自动绑定,因此实例方法的调用者是类本身,而不是对象。

  3. 如果程序依然希望使用类来调用实例方法,则必须手动为方法的每一个参数传入参数值。

类变量和实例变量

变量

通常来说,实例变量是对于每个实例都独有的数据,而类变量是该类所有实例共享的属性和方法。

  • 类变量又叫全局变量,是属于类的特性,实例先找实例化变量,然后再去找类变量
  • 类变量可以用实例去调用
  • 如果类变量有多重继承关系, 就需要按照指定的路线进行查找

下面代码定义了一个 Address 类,并为该类定义了多个类变量:

class Address :
    detail = '广州'
    def __init__(self, detail):
        # 尝试直接访问类变量
        #print(detail) # 报错
        # 通过类来访问类变量
        print(Address.detail)    # 输出 广州

# 通过类来访问Address类的类变量
print(Address.detail)
addr = Address('广州')
# 修改addr实例的变量
addr.detail = '佛山'
print(Address.detail)    # 输出 广州
print(addr.detail)    # 输出 佛山
  1. 因为动态语言的特性, addr.detail = '佛山' 并没有改变类变量,只是新增了一个实例变量
  2. 在实例方法内部,可以通过 类名.类变量 来访问类变量,还可以使用 self__class__.类变量 来访问类变量

四、实例方法与类方法、静态方法

实例方法
普通实例方法,第一个参数需要是 self,它表示一个具体的实例本身。

class  Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    def __str__(self):
        return ("{year}-{month}-{day}").format(year=self.year,month=self.month,day=self.day)

    def yesterday(self):
        self.day-=1

date=Date(2018,10,20)
print(date)
date.yesterday()
print(date)

类方法
在一个方法的上面,加上装饰器 @classmethod 就表示这个方法是类方法。
对于类方法,它的第一个参数不是self,是cls,它表示这个类本身。

静态方法
在一个方法的上面,加上装饰器 @staticmethod 就表示这个方法是静态方法。
静态方法对参数没有要求,可以不传。

class Foo(object):   
    X = 1
    Y = 14

    @staticmethod
    def averag(*mixes):
        # "父类中的静态方法"
        return sum(mixes) / len(mixes)

    @staticmethod
    def static_method():
       # "父类中的静态方法"
       print ("父类中的静态方法")
       return Foo.averag(Foo.X, Foo.Y)

    @classmethod
    def class_method(cls):
       # 父类中的类方法
       print ("父类中的类方法" )
       return cls.averag(cls.X, cls.Y)

class Son(Foo):
     X = 3
     Y = 5

     @staticmethod
     def averag(*mixes):
        # "子类中重载了父类的静态方法"
       print ("子类中重载了父类的静态方法")
       return sum(mixes) / 3

p = Son()

print (p.averag(1,5))    # 最后一段,结果为2.0
print (p.static_method())    # static_method段,结果为7.5
print (p.class_method())    # class_method段,最后一段,结果为2.6

五、成员可见性

成员的可见性

  • 公开的,外部可以访问的,public。
  • 私有的,外部不可访问的,private。

可见性关键字

  • 其它语言中使用public和private关键字,python中没有
  • python中使用 __ 双下划线区分公开和私有,__ 表示成员是私有的
class Student():
    sum1 = 1
    def __init__(self,name,age):
        self.name = name
        self.age = age
        self.score = 0   #增加变量score,并给他初始值
    def __marking(self,score):
        #加双下划线,变为私有,在外部不能引用
        if score < 0:
            return "该同学的成绩不正常"
        self.score = score
        print(self.name,"同学本次考试的成绩为:",self.score)

student = Student('李华',18)
result = student.__marking(10)
print(result)

执行后 'Student' object has no attribute '__marking' 错误,因为 __marking 对于 Student 不可见
通过 student.__dict__ 以及 dir(student) 两个内置的函数可以查看到stu对象的所有数据成员和方法

{'name': '李华', 'age': 18, 'score': 0}
['_Student__marking', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', 
'__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', 
'__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'score', 'sum1']

可以看到原本的 __marking 变成了 _Student__marking

访问私有成员

class Address :
    def __init__(self, detail):
        self.__detail = detail

addr = Address('广州')
addr.__detail = '佛山'
print(addr.__dict__)    # {'_Address__detail': '广州', '__detail': '佛山'}
print(addr._Address__detail)    # 广州

私有变量可以使用 _Address__detail 进行强制访问
私有变量不可见,但是对其进行赋值操作,并不会报错,这是因为Python动态语言的特性, 赋值只是新增了一个实例变量,并没有改变原有的私有变量

你可能感兴趣的:(13--Python 面向对象基础)