• 概述:类的特点
  • 作用域和命名空间。
  • 类的详解:(python官方教程摘录)

概述特性

Python在oop方面思想和Ruby一样。同样包括数据封装,继承和多态三大特点。

Python的类提供了面向对象编程的所有标准特性:

  • 类继承机制允许多个基类, ⚠️这点和Ruby不一样,Ruby要求继承的关系是只能有一个父类。
  • 派生类可以覆盖它基类的任何方法,一个方法可以调用基类中相同名称的的方法。
  • 对象可以包含任意数量和类型的数据。
  • 在运行时创建,也可以在创建后修改。

例子:

class Student(object):
"""docstring for Student.""" def __init__(self, name, score):
super(Student, self).__init__()
self.name = name
self.score = score def print_score(self):
print('%s %s' % (self.name, self.score)) bart = Student('Bart Simpson', 59)
lisa = Student('Lisa Simpson', 87)
bart.print_score()
lisa.print_score()
  • 第一行 class Student(object)表示类Student从object继承过来。用的是括号。object类是一切类的父类。
  • 实例一个Student对象的代码类似调用一个函数。⚠️这点和Ruby的写法不一样。(Ruby用到了new关键字)

上面的例子实例对象有两个变量name,score:

>>> bart.name = 'Bart Simpson'
>>> bart.name
'Bart Simpson'

⚠️又和Ruby不同, ruby必须提供name的读方法和写方法。Python不用。

  • __int__方法是类定义的初始化方法,第一个参数self代表类的实例。
  • 类中定义的实例方法,第一个参数永远是self。调用时,无需传递该参数。

数据封装

类内部定义的方法可以访问类实例的数据,无需类的外部调用一个方法来读取数据,这就是数据封装。这些封装数据的函数就是类的方法。

上面的print_score就是类的方法,调用时无需传递参数。

⚠️Python的类的实例对象可以绑定任何外部数据:

>>> bart = Student('Bart Simpson', 59)
>>> lisa = Student('Lisa Simpson', 87)
>>> bart.age = 8 #age并不是类中定义的属性。
>>> bart.age
8
>>> lisa.age
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute 'age'

访问限制

用__作为变量前缀的变量,不能被外部直接读取。会报告错误❌。⚠️是两个下划线。

⚠️__xx__这是特殊变量没有访问限制

例子:实例变量.__name

只能通过类内定义一个读取变量的方法来实现。

原理:

python会自动把__xxx这类私有变量,的名字转化为_类名__xxx.所有直接访问是不行的。

⚠️不要在外部直接修改私有变量!!!

实例的方法/函数

内置函数,用于操作实例对象。

  • hasattr(object, name)   参数有2个。如果对象的属性中有name这个字符串, 则返回True。
  • getattr()
  • setattr()

类属性

>>> class Student(object):
... name = 'Student'
>>> s = Student() # 创建实例s
>>> print(s.name) # 打印name属性,因为实例并没有name属性,所以会继续查找class的name属性
Student

⚠️Ruby中定义类属性用@@,例子:@@name。实例变量用@, 例子@xxx。


Python的作用域和命名空间

标签:scope rules,高级Python程序员。

定义

namespace is a mapping from names to objects.

命名空间是一个从名字到对象的映射(指向,明确的路径)。

Most namespaces are currently implemented as Python dictionaries。

大部分都是由字典dict(其他语言也称为hash)实现。

命名空间namespace例子:

  • 内置函数名字和内置表达式名字的集合。
  • 模块中的global names
  • 一次函数调用内的local names
  • 一个对象的属性集合也是一种namespace

注意:不同namespace内的名称之间没有任何关系。

创建命名空间和lifetimes生命周期

在不同的情况下,命名空间被创建并有了各不相同的生命周期。例子;

  • 包括内建函数的命名空间会在Python interpreter启动时创建,并且永远不被删除。
  • 一个模块的全局命名空间在这个模块定义被读入的时候创建。一般会持续到解释器退出。
  • 一个函数的local namespace在函数被调用时创建,在函数返回或抛出一个不在函数内处理的错误时被删除。

作用域

scope is a textual region of a Python program where a namespace is directly accessible.

一个作用域是一个Python程序的文本区域,这个区域的namespace是可以直接访问的。

在执行中,有几种内嵌作领域的命名空间可以被直接访问:

  • 最内部的作用于,它被首先搜索,它包括了local names
  • 任何闭包函数的作用领域,它会被从最近的封闭作用领域开始搜索,包括non-local,也包括non-global names.
  • the next-to-last scope, 包括当前模块的全局名称
  • 最外面的作用领域,被最后搜索,这个命名空间包括内建函数。

命名空间的查找顺序:

 Local首先搜索,包含局部名字的最内层(innermost)作用域,如函数/方法/类的内部局部作用域;

 Enclosing根据嵌套层次从内到外搜索,包含非局部(nonlocal)非全局(nonglobal)名字的任意封闭函数的作用域。如两个嵌套的函数,内层函数的作用域是局部作用域,外层函数作用域就是内层函数的 Enclosing作用域;

 Global倒数第二次被搜索,包含当前模块全局名字的作用域;

 Built-in最后被搜索,包含内建名字的最外层作用域。

Python按照以上LEGB的顺序依次在四个作用域搜索名字,没有搜索到时,Python抛出NameError异常。

用一个类比来理解命名空间与作用域:

  四种作用域相当于我们生活中的国家(Built-in)、省(Global)、市(Enclosing)、县(Local),命名空间相当于公务员花名册,记录着哪个职位是哪个人。国家级公务员服务于全国
民众(全国老百姓都可以喊他办事),省级公务员只服务于本身民众(国家层面的人或者其他省的人我不管),市(Enclosing)、县(Local)也是一个道理。当我们要找某一类领导(例如想找
个警察帮我打架)时(要访问某个名称),如果我是在县(Local)里头,优先在县里的领导花名册中找(优先在自己作用域的命名空间中找),县里花名册中没警察没有就去市里的花名册找(往
上一层作用域命名空间找),知道找到国家级都还没找到,那就会报错。如果省级民众想找个警察帮忙大家,不会找市里或者县里的,只会找自己省里的(其它省都不行),或者找国家级的。国家、
省、市、县肯定一直都在那里,可不会移动(作用域是静态的);领导可以换届,任期移到就换人(命名空间是动态的,每次调用函数都会新的命名空间,函数执行

Python的特殊之处

如果不存在生效的 global 语句 -- 对名称的赋值总是进入最内层作用域。

赋值不会复制数据 --- 它们只是将名称绑定到对象。 删除也一样,del x会从局部命名空间的引用中删除对x的绑定。

所有引入的新名称的操作都使用局部作用领域。import声明和函数定义,会在local scope中绑定module或函数名称。

#gloal 用于表示变量生存在全局作用域,并且应当被重新绑定。顶层的绑定。
#nonlocal 表示变量生存在当前作用域的外部,并且应当在其中被重新绑定。

glocal与nonlocal的区别

第一,两者的功能不同。

global关键字修饰变量后标识该变量是全局变量,对该变量进行修改就是修改全局变量,而nonlocal关键字修饰变量后标识该变量是上一级函数中的局部变量,如果上一级函数中不存在该局部变量,nonlocal位置会发生错误。

def f1():
# i = 1
def f2():
nonlocal i
print(i)
i = 2
f2()
print(i) f1()
# SyntaxError: no binding for nonlocal 'i' found

第二,两者使用的范围不同。

  • global关键字可以用在任何地方,包括最上层函数中和嵌套函数中,即使之前未定义该变量,global修饰后也可以直接使用。
  • nonlocal关键字只能用于嵌套函数中,并且外层函数中定义了相应的局部变量,否则会发生错误。
i = 0
def f1():
i = 1
def f2():
global i #重新绑定
print("global", i)
i = 2
f2()
print("local",i) f1()
# 输出:
# global 0
# local 1

作用域是按字面文本来确定的:

  • 在一个模块内定义的函数的全局作用域就是该模块的命名空间,无论该函数从什么地方或以什么别名被调用。
  • 即在函数定义时,就已经确认作用域了。和函数调用时的位置无关!

几个例子:

i = 1

def f1():
print(i) def f2():
i = 2
f1() f2() print(i) #输出
#1
#1

解释:这个例子,说明了函数的外部作用域,是函数定义时的外部代码。

if True:

  i = 1

print(i) # 可以正常输出i的值1,不会报错

解释:这说明了if语句不产生新的作用域, 只有函数/类才会产生新作用域。

for i in range(10):

  pass

print(i) #输出结果是9,而不是NameError

解释: 无关作用域,i被声明,并被赋值。

def test():

    print(i)

  i= 2

i = 0

test()

解释:会报告错误,UnboundLocalError: local variable 'i' referenced before assignment。i已经被占用了。所以不能再被重新声明。

class A(object):

    a = 2

    def fun(self):

        print(a)

new_class = A()

new_class.fun()
#NameError: name 'a' is not defined

解释:还不理解。fun函数的外部作用域中,没有a。


类的详解

类定义

  1. 当进入类定义,创建一个命名空间,作为局部变量和函数的局部作用域。
  2. 离开类定义,会创建一个类对象。a class object。
  3. 类名称和这个类对象绑定在一起。

Class Object

类对象有2种操作,属性引用和实例化。类变量和函数都是属性。

Instance Object

可以对实例对象进行实例属性引用,有2种:

  • data attributes: 在其他语言叫做实例变量instance variables(Ruby, Smalltalk)/ data members(C++)
  • methods:就是从属于一个对象的函数。

例子:

class MyClass:
i = 12345 def f(self):
return 'hello world'
x = MyClass()

x是类的实例对象,x.f是一个method object。具体来说是一个绑定:

<bound method Myclass.f of <__main__.Myclass object at 0x10c6f26d0>>

而MyClass.f是一个function object。<function Myclass.f at 0x10c759c10>

Python的类的实例对象调用方法,会把自身作为第一个参数传入。

解释一下什么是方法对象method object

比如本例子

x.f()
  1. 首先,实例x的类会在自身作用域搜索Myclass.f,如果f是一个函数对象,则
  2. 创建一个method object:它是通过把实例对象x和函数对象f打包到一起后产生的一个抽象的对象。
  3. 当x.f()被调用,会产生一个新的参数list,这个list会提供给Myclass.f。

私有变量和名称改写name mangling

Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped.

任何形式为 __spam 的标识符(至少带有两个前缀下划线,至多一个后缀下划线)的文本将被替换为 _classname__spam,其中 classname 为去除了前缀下划线的当前类名称。

请注意,改写规则的设计主要是为了避免意外冲突;

再谈迭代器

for element in [1, 2, 3]:
print(element)

解释:

实际上for语句把[1,2,3]作为参数,传入iter()方法。返回一个定义了__next__()方法的对象,叫做iterator object。

每次使用next(),会返回一个容器中的元素。知道元素用尽,引发StopIteration异常。

类可以通过定义__iter__和__next__函数,就可以对类的实例对象进行定制化了。例子:

class Reverse:
def __init__(self, data):
self.data = data
self.index = len(data) def __iter__(self):
return self def __next__(self):
if self.index == 0:
raise StopIteration
self.index = self.index - 1
return self.data[self.index] rev = Reverse('spam') for char in iter(rev):
print(char)

最新文章

  1. http状态消息
  2. Source Insight 基本使用(2)-修改Source Insight 快捷键
  3. Quartz.net打造信息抽取器
  4. WiFi Test Entity
  5. 警惕arm-linux-gcc编译器优化选项
  6. MVC5框架解析之MvcHandler
  7. Easyui 异步树直接所有展开
  8. owa_outlook暴力破解脚本
  9. Django的ModelForm组件
  10. Callable抛出异常与future.get
  11. Windows下javac不可用,java -version可以
  12. EF Core Fluent API
  13. pytorch实现AlexNet网络
  14. RabbitMQ(四):RPC的实现
  15. 解决idea中Activiti的bpmn编辑器的中文乱码问题
  16. Qt5_vs2013_error_C2001: 常量中有换行符__ZC
  17. Tarjan 算法详解
  18. 基于FPGA的PCIe接口实现(具体讲解了数据流向)
  19. STM32从boot跳转到app失败
  20. kali2.0 设置输入法 找了好久,亲测有效

热门文章

  1. 解决windows server 2019远程桌面许可证问题
  2. JSP基础知识补充
  3. ZOJ Problem Set - 1006
  4. 17.Azkaban实战
  5. sqarkSQL hiveSql
  6. js预解析实例
  7. Hyperledger Fabric-sdk-java
  8. STM32F10xxx_异常与中断
  9. ES6入门五:箭头函数、函数与ES6新语法
  10. 微信小程序iOS下拉白屏晃动,坑坑坑