面向对象初接触(上)

面向对象是一种认知世界,分析世界的方法论,将万事万物抽象为类
类class :类是抽象的概念,是万事万物的抽象,是一类事物的共同特征的集合,在计算机语言里就是 属性和方法的集合
 
对象instance object
对象是类的具象,是一个实体
 
属性,他是对象状态的抽象,用数据结构来描述
操作,他是对象行为的抽象,用操作名和实现该操作的方法来描述
 
哲学:
一切皆对象
 
对象是数据和操作的封装
 
对象是独立的,但是对象之间可以相互作用
 
 
目前OOP是最接近人类认知的编程方式
 
 
 
面向对象3要素
1,封装
     组装将数据和操作组装到一起
     隐藏数据:对外只暴露一些接口,通过接口访问对象
 
2,继承
     多复用,继承来的就不用自己在写
     多继承少修改,OCP,     使用继承来改变,来体现个性
 
3,多态
     面向对象编程最灵活的地方,动态绑定
 
python的类
定义 class ClassName :
     语句块
1,必须使用class关键字
2,类名必须是大驼峰命名
3,类定义完成后,就产生了一个类对象,绑定到了ClassName上
 
例子(1,0)
class MyClass : 
     """ A example class"""
     x = 'abc'          #类属性
     def foo(self) :      #类属性foo 也是方法
          return 'My Class'
print(MyClass.x)
print(MyClass.foo)
print(MyClass.__doc__)
 
类对象及类属性
     类对象,类的定义就会生成一个类对象     
     类的属性, 类定义中的变量和类中定义的方法都是类的属性
     类变量,x是MyClass的变量
 
MyClass中,x,foo都是类的属性,__doc__也是类的属性
 
foo方法是类的属性,需要实例的调用才能使用
 
foo是method方法对象,不是普通的函数对象function了,他至少有一个参数,且第一个参数必须是self(self可以换名字),整个参数位置就留给了self
 
self      指代当期实例本身
 
实例化
 
a = MyClass(  ) #实例化
 
使用这个语法就 在类对象名称后面加上一个括号,就调用类的实例化方法,完成实例化
 
实例化就真正创建一个该类的对象(实例),例如 tom jerry
 
实例化后获得的实例,是不同的实例,即使是使用同样的参数实例化,也得到不同的对象
 
python类实例化后,会自动调用__init__方法,整个方法第一个参数必须留给self , 其他参数任意
 
 
例子(1,1):
 
class Person:                            #定义类
 
   age = 20                              #类的属性
 
   def __init__(self,name):          #初始化属性值,也是类的方法or属性
        self.name = name
 
   def foo(self):                        #类的方法
        return 'Person'
 
Person.age = 353                    #可在外部通过类直接修改其内部的属性
 
tom = Person('tom')               #实例化!!!!!!!!!
 
jerry = Person('jerry')              #实例化 , 不是一个对象,它们的id不一样
 
print(tom.age ,jerry.age)         #若初始化后没有age整个元素,那么就会去类的内部查找;
 
                                             # 若类里没有,会直接new一个类属性到类的属性字典中
#print(Person.name)              #类没有名字
 
print(Person.__dict__)             #查看Person类的属性字典
 
print(tom.foo())                     #如何去调用一个类里面的类的方法
 
Pe = Person( )                       #会 调用__init__
 
__init__方法
例子(1,1)中 : 
 
Pe实际上调用的是__init__(self)方法,可以不定义,如果没有定义,会在实例化后隐式调用
他的作用: 对实例初始化
!!!注意:
初始化函数可以有很多的参数,但是第一个位置必须是 self,例如init(self,name,age)
__init__( ) 方法不能有返回值,也就是说只能是None!
 
实例对象instance
 
类实例化后一定会获得一个对象,就是实例对象
(1,1)中,tom jerry就是Person类的实例
 
__init__方法的第一参数self就是指代某一个实例
 
类实例化出一个实例对象,实例对象会绑定方法,调用对象时会采用 jerry.showage( )的方式
 
定义的showage(self)这个self就是jerry,python会把方法的调用者作为第一参数self的实参传入
 
self.name就是jerry对象的name , name是保存在了jerry对象上,而不是在Person类上,所以称为实例变量
 
self
class Person:
   def __init__(self):
        print('{}'.format(id(self)))
 
c = Person()     #会调用__init__
print('c ={}'.format(id(c)))
打印结果为:
2322420470280
c =2322420470280
上例说明,self就是调用者,就是c对应的实例对象
self 整个名字只是一个管理,它可以修改,为了代码的可读性,不要去修改
 
实例变量和类变
 
实例变量是每一个实例自己的变量,是自己独有的;类变量是类的变量,是类的所有实例共享的属性和方法
 
                                            特殊属性
                                           含义
__name__
对象名
__class__
对象的类型
__dict__
对象的属性的字典
__qualname__
类的限定名
 
举例
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)
Person.age = 30
print(Person.age,tom.age,jerry.age)
print(tom.height,jerry.height,Person.height)
jerry.height = 175 #修改jerry字典中的数据
print(Person.height,tom.height,jerry.height)
tom.height += 10
print(Person.height,tom.height,jerry.height)
Person.height +=15
print(Person.height,tom.height,jerry.height)
Person.weight = 70
print(Person.weight,tom.weight,jerry.weight)
 
print(tom.__dict__['height'])
print(tom.__dict__['weight']) #weight属于类的字典里的属性,不属于tom的字典里的属性,所以会出错
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)
Person.age = 30
print(Person.age,tom.age,jerry.age)
print(tom.height,jerry.height,Person.height)
jerry.height = 175 #修改jerry字典中的数据
print(Person.height,tom.height,jerry.height)
tom.height += 10
print(Person.height,tom.height,jerry.height)
Person.height +=15
print(Person.height,tom.height,jerry.height)
Person.weight = 70
print(Person.weight,tom.weight,jerry.weight) #tom和jerry的属性字典里没有weight元素,会在类的属性里查找
 
print(tom.__dict__['height'])
print(tom.__dict__['weight']) #weight属于类的字典里的属性,不属于tom的字典里的属性
#总结
#是类的,也是这个类所有实例的,其实例都是可以访问到的;是实例的,就是这个实例自己的,通过类访问不到
#类的变量,类下的方法都可以拿来用,但是不存在于类下的方法字典
#类变量是属于类的变量,这个类的所有实例可以共享这个变量
#实例可以动态的给自己增加一个属性。实例.__dict__[变量名]和实例.变量名都可以访问到
#实例的同名变量会隐藏这类变量,或者说是覆盖了这个类变量
 
实例属性的查找顺序
 
#指的是实例使用.来访问属性,会先找自己的__dict__,如果没有,通过属性__class__找到自己的类,再去类的.__dict__中找
#如果实例 使用__dict__[变量名]访问变量,就不会按照上面的查找顺序找变量了
#一般来说 , 类变量使用全大写来命名
 
#装饰一个类
#增加类的变量
# def add_name(name,clz):
#       clz.NAME = name #动态增加属性
#改成装饰器
# def add_name(name):
#         def add_arg(clz):
#             clz.NAME = name
#             return clz
#         return add_arg
#
# @add_name('tom')
# class Person:
#         AGE = 3
#
# print(Person.__dict__)
#之所以能够装饰,本质上是为类对象动态的添加了一个属性,而Person这个标识符指向这个类对象
 
#类方法和静态方法
#前面的例子中定义的__init__等方法都是类的属性第一个参数必须是self。而self必须指向一个对象,也就是类必须实例化之后,由实例来调用这个方法
 
#普通函数:
class Person:
   def normal_method(self):
   print('nomal')
 
print(Person.__dict__)
 
Person.normal_method()
#这个方法只是被Person这个名词空间管理的一个普通的方法,normal_method这是Person的一个属性而已
#由于normal_method 在定义的时候没有指定self,所以不能完成实例对象的绑定,不能用Person().normal_method()调用
# !虽然语法没问题,但是没人这么写,所以禁止这样写
 
# 类方法
 
class Person:
   @classmethod
   def class_method(cls):
         print('class={0.__name__}{0}'.format(cls))
         cls.HEIGHT = 170
 
Person.class_method()
print(Person.__dict__)

 

#类方法
  #1,在类定义中,使用@classmethod装饰器修饰的方法
  #2,必须至少有一个参数,且第一个参数留给了cls,cls指代调用者即类对象自身
  #3,cls这个标识符可以是任意合法名称,但是为了易读,请不要修改
  #4,通过cls可以直接操作类的属性,但是不能操作类的实例
 
#静态方法
# class Person:
#    @classmethod
#     def class_method(cls): #cls是啥?
#             print('class = {0.__name__}{0}'.format(cls))
#             cls.HEIGHT = 170
#
#         @staticmethod #静态方法
#         def static_methd():
#             print(Person.HEIGHT)
#Person.class_method()
#Person.static_methd()
#print(Person.__dict__)
#静态方法
# 在类定义中,使用@staticmethod装饰器修饰的方法
# 调用时,不会隐式的传入参数
  #静态方法,定位于类定义的命名空间中,他不会对任何实例类型进行操作,类对象和实例都可以调用静态方法
 
#方法的调用
#怎么去调用一个类方法
# class Tu:
#      def __init__(self,name,age =18):
#             self.name = name
#             self.age = age
#         def hi(self): #实例方法会去外部调用缺省的参数
#             print(self.name)
# print(1,Person.normal_method()) #可以调用,并执行normal_method()方法,
# print(2,Person.method()) #需要一个实例参数
# print(3,Person.class_method()) #可以调用,因为动态方法,将其类本身传给了这个方法
# print(4,Person.static_methd()) #静态属性不能调用类或者实例的方法
# x = Tu('heihei')
# print(x.hi())
类对象可以调用类方法,类对象不能调用实例的方法;
实例对象可以调用类方法,和实例的方法
实例方法,第一个参数必须默认传实例对象,一般习惯用self
静态方法,参数没有要求     静态方法可以在其内部定义需要做的操作,因此可以不需要特定的去传参数
类方法,第一个参数默认必须是传 类 ,一般习惯用cls
 
#访问控制
#私有属性
# class Person:
#         def __init__(self,name,age=18):
#             self.name = name
#             self.age = age
#         def group(self,i = 1):
#             if i >0 and i < 150: #控制逻辑
#                 self.age += i
 
# p1 = Person('tom')
# p1 .group(20) #正常的范围
# p1.age = 160 # 超过了范围,并绕过了控制逻辑
# print(p1.age)
 
#本来是想控制属性范围,结果在外界直接就被绕过逻辑直接改了,这样非常危险。但是python给我们提供了相关的解决办法
 
#私有属性
#使用双下划线开头的属性名,就是私有属性
# class person:
#         def __init__(self,name,age= 18):
#             self.name = name
#             self .__age = age
#         def group(self,i = 1):
#             if i >0 and i<150:
#                 self.__age += i
# p1 = person('tom')
# p1.group(20)
# print(p1.__age)
#外部已经访问不到__age了,怎么访问这个变量呢?
# class Person:
#         def __init__(self,name,age=18):
#             self.name = name
#             self.__age = age
#         def group(self,i=1):
#             if i > 0 and i<150:
#                 self.__age+=i
#         def getage(self):
#             return self.__age
#
#
# print(Person.getage.__dict__)
#如何动态的增加一个看不到的参数呢?
# class Person:
#         def __init__(self,name,age =18):
#             self.name = name
#             self.__age = age
#
#         def growup(self,i=1):
#             if i> 0 and i <150:
#                 self.__age += i
#         def getage(self):
#
#             return self.__age
#
# p1 =Person('tom')
# print(p1.growup(20))
# p1.__age = 28
# print(p1.__age)
# print(p1.getage())
# print(type(p1))
# print(p1.__dict__)
#私有变量的本质
#类定义的时候,如果声明一个实例变量的时候,使用双下划线,Python解释器会将其改名,转换名称为_类名__变量名的名称,所以用原来的名字访问不到
#但是直到了私有变量的名称后,通过实例还是可以对其的属性进行修改
 
#保护变量
#在变量名前使用一个下划线,称为保护变量
# class Person:
#         def __init__(self,name,age = 18):
#             self.age = age
#             self._name =name #保护变量
#
# tom = Person('tom')
# print(tom._age)
# print(tom.__dict__)
#可以看出,这个_age属性根本就没有改变名称,和普通的属性一样,解释器不做任何处理,是开发者的约定
 
#私有方法
#和保护变量差不多,多了双下划线命名
class Person:
  def __init__(self,name,age=18):
  self.name = name
  self._age = age
 
  def _getname(self):
  return self.name
 
  def __getage(self):
  return self._age
tom = Person('tom')
# print(tom._getname()) #没改名4
# print(tom.__getage()) #没有这个属性
print(tom.__dict__)
print(tom.__class__.__dict__)
print(tom._Person__getage()) #改名了
#私有方法的本质,单下划线的方法只是开发者之间的约定,解释器不做任何结婚死
#双下划线的方法,是私有方法,解释器会改名,改名策略和私有变量有关,_类__变量名
#方法变量都可以在类的__dict__中找到
 
#总结:
#在Python中使用_单下划线或者__双下划线来标识一个成员被保护,或者被私有化隐藏了起来
#Python没有绝对的安全的保护成员或者私有成员,并不能真正的阻止用户修改类的成员
#前导的下划线只是一种警告或者提醒,一般都是遵守约定,除非真有必要,不要修改或者使用保护成员或者私有成员
相关参考网站:
https://github.com/pythonpeixun/article/blob/master/python/python_classmethod_staticmethod.md#%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95%E7%B1%BB%E6%96%B9%E6%B3%95%E4%BD%BF%E7%94%A8%E5%8C%BA%E5%88%AB%E6%88%96%E8%80%85%E8%AF%B4%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF
面对对象多态参考网站:
blog.csdn.net/shangzhihaohao/article/details/7065675

posted on 2017-11-12 17:34  pythonerLau  阅读(241)  评论(0编辑  收藏  举报

导航